# Gym102832K. Ragdoll (CCPC Changchun)

Gym102832K. Ragdoll (CCPC Changchun)

## Meaning:

n points, each point has its own weight a i a_i ai, at the beginning, the i-th point is in the i-th set.
If a pair of points (i,j) is bad if and only if i and j are in the same set, and g c d ( a i , a j ) = a i ⊕ a j gcd(a_i,a_j)=a_i⊕a_j gcd(ai​,aj​)=ai​⊕aj​
There are now three operations:

1. Add a point x (in a new set) with a value of v
2. Merge two sets x and y
3. Change the value of point x to v

There are m operations. Please output how many pairs of nodes are bad after each operation

## Solution:

I had no way to start at first, because I didn't know how to apply this formula
Here's a clever conversion, a i ⊕ a j ​ ⩾ ∣ a i − a j ∣ ​ ⩾ g c d ( a i , a j ) a_i⊕a_j ​⩾|a_i-a_j|​⩾gcd(a_i,a_j) ai ⊕ aj ⩾∣ ai − aj ∣⩾ gcd(ai, aj), if we want the equation to hold, we need to meet:

1. a i ​ ⊕ a j = ∣ a i − a j ∣ a_i​⊕a_j=|a_i-a_j| ai​​⊕aj​=∣ai​−aj​∣
2. ∣ a i − a j ∣ = g c d ( a i , a j ) |a_i-a_j|=gcd(a_i,a_j) ∣ai​−aj​∣=gcd(ai​,aj​)

Conversion certificate:
a ⊕ b > = a − b a⊕b>=a-b A ⊕ b > = a − B, this formula is obviously true. Consider it with binary thought
g c d ( a , b ) < = a − b gcd(a,b)<=a-b gcd(a,b) < = a − B: since gcd(a,b) is the factor of B and gcd(a,b) is the factor of a, gcd(a,b) is the factor of (a-b), so gcd(a,b) must be less than or equal to a-b

∣ a i − a j ∣ = g c d ( a i , a j ) |a_i-a_j|=gcd(a_i,a_j) ∣ai​−aj​∣=gcd(ai​,aj​)
a j = a i + g c d ( a i , a j ) a_j=a_i+gcd(a_i,a_j) aj = ai + gcd(ai, aj) or a j = a i − g c d ( a i , a j ) a_j=a_i-gcd(a_i,a_j) aj​=ai​−gcd(ai​,aj​)
and g c d ( a i , a j ) gcd(a_i,a_j) gcd(ai, aj) is a i a_i Factor of ai +
So we can enumerate a i a_i ai, and then enumerate a i a_i The factor of ai ， so we find the factor for a i a_i Legal for ai a j a_j aj, save it with vector. It means that we can preprocess all the bad point pairs
Let's look at three more operations. This kind of set operation can be maintained by merging and querying sets
For operation 1, a new set is added
For operation 2, merge two sets. If we merge directly and blindly, the complexity is O(n). Here, heuristic merging is used to merge small sets into large sets, so that the complexity is reduced to O(log)
For operation 3, we first remove the possible bad point pair composed of the original elements, change it to the number v, and then add a new bad point pair
In each operation, the answer is maintained. How to maintain it? Because we preprocess all point pairs. When merging two sets, we enumerate all elements of the small set to see how many can form bad point pairs in the large set, and update the answer ans. When you modify the amount, you delete the original and add a new one
unordered_map faster
See the code for details

## code:

#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{
x= 0;
char c= getchar();
bool flag= 0;
while (c < '0' || c > '9')
flag|= (c == '-'), c= getchar();
while (c >= '0' && c <= '9')
x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();
if (flag)
x= -x;
}
template <typename T> inline void write(T x)
{
if (x < 0) {
x= ~(x - 1);
putchar('-');
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef ONLINE_JUDGE
#else
startTime = clock ();
freopen("data.in", "r", stdin);
#endif
}
void Time_test()
{
#ifdef ONLINE_JUDGE
#else
endTime= clock();
printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn=5e5+9;
int n,m;
int a[maxn];
vector<int>g[maxn];
int fa[maxn];
int sz[maxn];
ll ans=0;
unordered_map<int,ll>mp[maxn];
int gcd(int a,int b){
if(b)return gcd(b,a%b);
return a;
}
int find(int x){
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void work(int x){
for(int i=1;i*i<=x;i++){//Enumeration factor
if(x%i==0){
int tmp=x-i;
g[x].push_back(tmp);

tmp=x+i;
if((tmp^x)==gcd(tmp,x)&&tmp<=200000)
g[x].push_back(tmp);

if(i!=x/i){
tmp=x-x/i;
if((tmp^x)==gcd(tmp,x)&&tmp!=0)
g[x].push_back(tmp);

tmp=x+x/i;
if((tmp^x)==gcd(tmp,x)&&tmp!=200000)
g[x].push_back(tmp);
}
}
}
}
void merge(int x,int y){
int fx=find(x);
int fy=find(y);
if(fx==fy)//If you exit in a collection
return ;
//Let's make fy a small set and merge fy into fx
if(sz[fx]<sz[fy])
swap(fx,fy);
for(auto& it:mp[fy]){
//Traverse all elements in the small collection, it.first
//		cout<<"it.first="<<it.first<<endl;
for(int i=0;i<g[it.first].size();i++){
//			printf("g[it.first].size()=%d\n",g[it.first].size());
//			printf("g[it.first][%d]=%d\n",i,g[it.first][i]);
//See how many points in the large set can form bad point pairs with the elements of the small set
if(mp[fx].count(g[it.first][i]))
ans+= 1ll * it.second * mp[fx][g[it.first][i]];
}
}
for(auto &it:mp[fy]){
//Store all the elements of fy in the set fx, that is, merge
mp[fx][it.first]+=it.second;
}
mp[fy].clear();
sz[fx]+=sz[fy];
fa[fy]=fx;
}
int main()
{
rd_test();
for(int i=1;i<=200002;i++){
}

cin>>n>>m;
for(int i=1;i<=n+m;i++){
fa[i]=i;
sz[i]=1;//Collection size
}
for(int i=1;i<=n;i++){
cin>>a[i];

mp[i][a[i]]++;//Each collection element and size
}
int op,x,y;
cin>>op>>x>>y;
if(op==1){
a[x]=y;
sz[x]=1;
mp[x][y]=1;
}
else if(op==2){//Merge operation
merge(x,y);
}
else if(op==3){//Modify value
int u=find(x);
for(int i=0;i<g[a[x]].size();i++){//Delete bad point pairs in the original set
if(mp[u].count(g[a[x]][i]))
ans-=mp[u][g[a[x]][i]];
}
mp[u][a[x]]--;//Point a[x] delete
a[x]=y;//Modify new value
for(int i=0;i<g[a[x]].size();i++){
if(mp[u].count(g[a[x]][i]))
ans+=mp[u][g[a[x]][i]];
}