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;
void read(){};
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;
    read(Ar...);
}
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;
			if((tmp^x)==gcd(tmp,x)&&tmp!=0)//bad compliant point pairs 
				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);
	//Update answer ans on merge 
	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++){
    	work(i);//Preprocess all bad point pairs 
	}
		
	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 
	}
	for(int i=1;i<=m;i++){//add to 
		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]]; 
			}
			mp[u][a[x]]++;//Add new points 
		}
		printf("%lld\n",ans); 
	}
	return 0;
    //Time_test();
}




Tags: Union Find

Posted on Wed, 20 Oct 2021 23:48:34 -0400 by anonymousdude