UOJ#33-[UR #2]树上GCD【长链剖分,根号分治】
正題
題目鏈接:https://uoj.ac/problem/33
題目大意
給出nnn個點的一棵樹
定義f(x,y)=gcd(dis(x,lca),dis(y,lca))f(x,y)=gcd(\ dis(x,lca),dis(y,lca)\ )f(x,y)=gcd(?dis(x,lca),dis(y,lca)?)。
對于每個iii求有多少對f(x,y)=i(x<y)f(x,y)=i(x<y)f(x,y)=i(x<y)
1≤n≤1051\leq n\leq 10^51≤n≤105
解題思路
首先肯定是枚舉lcalcalca節點,然后看他子樹里的情況,比較麻煩的是gcdgcdgcd剛剛好是ddd,但是其實我們可以是ddd的倍數的情況,然后后面再容斥出答案。
如果,然后暴力算的話首先需要一個長鏈剖分,然后每次是lenloglenlen\ log\ lenlen?log?len的。
但是仔細想一想就會發現這個復雜度其實是假的,因為每次暴力算的話的lenlenlen是這條鏈上面那條鏈的lenlenlen。
考慮點其他做法,因為是枚舉倍數,我們可以上我們的根號分治
對于dis>ndis>\sqrt ndis>n?的情況,我們之間暴力枚舉倍數,因為這樣不會超過n\sqrt nn?次
對于dis≤ndis\leq \sqrt ndis≤n?的情況,我們考慮儲存一些東西,設gi,jg_{i,j}gi,j?表示當前的鏈中depdepdep模iii為jjj的點的個數,然后處理的時候我們就可以直接用這個來計算了。
這樣平衡下來時間復雜度就是O(nn)O(n\sqrt n)O(nn?)了
code
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cctype> using namespace std; const int N=2e5+10; struct node{int to,next; }a[N]; int n,T,len[N],dep[N],h[N],g[400][400]; int tot,t[N],ls[N],son[N],*f[N],*now; long long ans[N],pre[N]; int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f; } void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot; } void dfs(int x){for(int i=ls[x];i;i=a[i].next){int y=a[i].to;dep[y]=dep[x]+1;dfs(y);if(len[y]>len[son[x]])son[x]=y;}len[x]=len[son[x]]+1;return; } void calc(int x,int top){f[x][0]=1;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==son[x])continue;f[y]=now;now+=len[y];calc(y,y);}if(son[x]){f[son[x]]=f[x]+1;calc(son[x],top);}for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==son[x])continue;for(int j=1;j<=len[y];j++)t[j]=f[y][j-1];for(int j=1;j<=len[y];j++)for(int k=2*j;k<=len[y];k+=j)t[j]+=t[k];for(int j=1;j<=len[y];j++)if(j>T){for(int k=j;k<len[x];k+=j)ans[j]+=1ll*f[x][k]*t[j];}else ans[j]+=1ll*g[j][dep[x]%j]*t[j];for(int j=1;j<=len[y];j++)f[x][j]+=f[y][j-1];for(int j=0;j<len[y];j++)for(int k=1;k<=T;k++)g[k][(j+dep[y])%k]+=f[y][j];}for(int i=1;i<=T;i++)g[i][dep[x]%i]++;if(x==top){for(int i=1;i<=T;i++)for(int j=0;j<len[x];j++)g[i][(j+dep[x])%i]=0;}return; } signed main() {n=read();for(int i=2;i<=n;i++)addl(read(),i);dfs(1);T=sqrt(n);if(T>350)T=350;f[1]=now=h;now+=len[1];calc(1,1);for(int i=n;i>=1;i--)for(int j=2*i;j<=n;j+=i)ans[i]-=ans[j];for(int i=2;i<=n;i++)pre[dep[i]]++;for(int i=n;i>=1;i--)pre[i]+=pre[i+1];for(int i=1;i<n;i++)printf("%lld\n",ans[i]+pre[i]);return 0; }總結
以上是生活随笔為你收集整理的UOJ#33-[UR #2]树上GCD【长链剖分,根号分治】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蟹和大闸蟹的区别 蟹和大闸蟹的区别介绍
- 下一篇: P4640-[BJWC2008]王之财宝