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:
- Add a point x (in a new set) with a value of v
- Merge two sets x and y
- 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:
- a i ⊕ a j = ∣ a i − a j ∣ a_i⊕a_j=|a_i-a_j| ai⊕aj=∣ai−aj∣
- ∣ 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(); }