Gym102832K. Ragdoll(CCPC长春)
Gym102832K. Ragdoll(CCPC長春)
題意:
n個點,每個點都有自己的權(quán)值aia_iai?,一開始第i個點在第i個集合里。
如果一對點(i,j)是bad當(dāng)且僅當(dāng)滿足i和j在同一個集合里,且gcd(ai,aj)=ai⊕ajgcd(a_i,a_j)=a_i⊕a_jgcd(ai?,aj?)=ai?⊕aj?
現(xiàn)在有3種操作:
有m個操作,請輸出每次操作后有多少對點是bad
題解:
一開始無從下手,因為這個式子不知道該如何應(yīng)用
這里有個很巧妙的轉(zhuǎn)換,ai⊕aj??∣ai?aj∣??gcd(ai,aj)a_i⊕a_j ??|a_i-a_j|??gcd(a_i,a_j)ai?⊕aj???∣ai??aj?∣??gcd(ai?,aj?),如果我們要讓等式成立,則需要滿足:
轉(zhuǎn)換證明:
a⊕b>=a?ba⊕b>=a-ba⊕b>=a?b,這個式子是顯然成立的,用二進(jìn)制思想去考慮
gcd(a,b)<=a?bgcd(a,b)<=a-bgcd(a,b)<=a?b:由于gcd(a,b)是b的因子,gcd(a,b)是a的因子,所以gcd(a,b)就是(a-b)的因子,所以gcd(a,b)一定小于等于a-b
∣ai?aj∣=gcd(ai,aj)|a_i-a_j|=gcd(a_i,a_j)∣ai??aj?∣=gcd(ai?,aj?)
aj=ai+gcd(ai,aj)a_j=a_i+gcd(a_i,a_j)aj?=ai?+gcd(ai?,aj?)或者aj=ai?gcd(ai,aj)a_j=a_i-gcd(a_i,a_j)aj?=ai??gcd(ai?,aj?)
而gcd(ai,aj)gcd(a_i,a_j)gcd(ai?,aj?)是aia_iai?的因子
因此我們可以枚舉aia_iai?,然后枚舉aia_iai?的因子,這樣就求出對于aia_iai?而言合法的aja_jaj?,用vector存下來。相當(dāng)于我們可以預(yù)處理出所有是bad的點對
我們再看三個操作,這種集合操作,都可以用并查集來維護(hù)
對于操作1,就是新加一個集合
對于操作2,合并兩個集合,如果我們直接盲目合并,復(fù)雜度是O(n),這里用到啟發(fā)式合并,把小的集合合并到大的集合中,這樣復(fù)雜度降低為O(log)
對于操作3,我們先去掉原先元素可能組成的bad點對,改成數(shù)v后再加入新的bad點對
在每次操作中,維護(hù)著答案,如何維護(hù)呢?因為我們預(yù)處理出所有點對,合并時兩個集合時,就枚舉小集合所有元素,看大集合內(nèi)有多少可以構(gòu)成bad點對,更新答案ans。修改數(shù)時就是刪除原先,加上新填
unordered_map速度更快
詳細(xì)內(nèi)容看代碼
代碼:
#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 #elsestartTime = clock ();freopen("data.in", "r", stdin); #endif } void Time_test() { #ifdef ONLINE_JUDGE #elseendTime= 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++){//枚舉因子 if(x%i==0){int tmp=x-i;if((tmp^x)==gcd(tmp,x)&&tmp!=0)//符合bad的點對 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)//如果在一個集合退出 return ;//我們讓fy是小集合,用fy去合并到fx if(sz[fx]<sz[fy]) swap(fx,fy);//合并時更新答案ans for(auto& it:mp[fy]){//遍歷小集合內(nèi)的所有元素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]);//看大集合中有多少點可以與小集合的元素組成bad點對 if(mp[fx].count(g[it.first][i]))ans+= 1ll * it.second * mp[fx][g[it.first][i]];}}for(auto &it:mp[fy]){//把fy的所有元素存放到集合fx中,即合并 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);//預(yù)處理出所有bad點對 }cin>>n>>m;for(int i=1;i<=n+m;i++){fa[i]=i; sz[i]=1;//集合大小}for(int i=1;i<=n;i++){cin>>a[i];mp[i][a[i]]++;//每個集合元素以及大小 }for(int i=1;i<=m;i++){//添加 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(x,y);}else if(op==3){//修改值 int u=find(x);for(int i=0;i<g[a[x]].size();i++){//刪除原集合內(nèi)的bad點對 if(mp[u].count(g[a[x]][i]))ans-=mp[u][g[a[x]][i]]; }mp[u][a[x]]--;//點a[x]刪除a[x]=y;//修改新值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]]++;//加上新點 }printf("%lld\n",ans); }return 0;//Time_test(); }總結(jié)
以上是生活随笔為你收集整理的Gym102832K. Ragdoll(CCPC长春)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 英特尔 i9-14900KS 处理器曝光
- 下一篇: 英雄联萌金卡牌攻略完全解析