題意:初始有 \(n\) 個節點 \(1\) 到 \(n\),權值分別為 \(a_1...a_n\)。
有三種操作,\(1.\)新建標號為 \(x\) 的節點,權值為 \(y\)。\(2.\) 合並標號為 \(x\) 和 \(y\) 所在的樹(集合)。\(3.\) 將標號為 \(x\) 的節點權值修改成 \(y\)。每次操作后詢問 \(\gcd(a_i, a_j) = a_i \oplus a_j\) 的對數,並且要求 \(i,j\) 在同一樹中。
對於 \(a_i\) 來說,不同的 \(\gcd\) 取值等於約數個數,考慮枚舉 \(a_i\) 的約數並判斷這個約數有沒有貢獻。具體的,
對於 \(a_i\) 的約數 \(x\),我們想找到一個值 \(y\) 使得 \(\gcd(a_i,x) = x = a_i \oplus y\),即 \(y=a_i \oplus x\),之后判斷 \(\gcd(a_i,y)\) 和 \(x\) 是否相等,進而得到 \(a_i\) 的一組解。
然后 \(\mathcal{O}(n\log^2 n)\) 的預處理貢獻和 \(\mathcal{O}(n\log n)\) 的啟發式合並就可以ac了,合並的時候大概有一個幾十的常數。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define debug(x) cerr << #x << " is " << x << '\n';
typedef long long LL;
typedef pair<int, int> pii;
typedef unsigned long long ull;
const int N = 5e5 + 5, M = 2e5, P = 1e9 + 7, INF = 0X3F3F3F3F;
const ull D = 743, mo1 = 966335533, mo2 = 966331133;
int n, q, u, v, a[N];
int fa[N], sz[N];
LL ans;
std::vector<int> g[N];
unordered_map<int, LL> dsu[N];
int find(int x) {
return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
for (int i = 1, y; i <= M; i++) {
for (int j = i + i; j <= M; j += i) {
y = (j ^ i);
if (i == __gcd(j, y)) {
g[j].pb(y);
}
}
}
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
dsu[i][a[i]]++;
}
for (int i = 1; i <= n + q; i++) {
fa[i] = i;
sz[i] = 1;
}
for (int i = 1, op, x, y; i <= q; i++) {
cin >> op >> x >> y;
if (op == 1) {
a[x] = y;
dsu[x][y]++;
cout << ans << '\n';
} else if (op == 2) {
u = find(x), v = find(y);
if (u != v) {
if (sz[u] > sz[v]) swap(u, v);
for (auto &it : dsu[u]) {
for (auto &it1 : g[it.fi]) {
if (dsu[v].count(it1)) ans += it.se * dsu[v][it1];
}
}
for (auto &it : dsu[u]) dsu[v][it.fi] += it.se;
dsu[u].clear();
fa[u] = v, sz[v] += sz[u];
}
cout << ans << '\n';
} else {
u = find(x);
for (auto &it :g[a[x]]) {
if (dsu[u].count(it)) ans -= dsu[u][it];
}
dsu[u][a[x]]--;
a[x] = y;
for (auto &it :g[a[x]]) {
if (dsu[u].count(it)) ans += dsu[u][it];
}
dsu[u][a[x]]++;
cout << ans << '\n';
}
}
return 0;
}
