http://uoj.ac/problem/33 (题目链接)
题意
给出一棵${n}$个节点的有根树,${f_{u,v}=gcd(dis(u,lca(u,v)),dis(v,lca(u,v)))}$,求对于${1<=i<=n-1,}$有多少${f_{u,v}=i}$。
Solution
虽然有官方题解,但是感觉写的并不是很详细→_→,不过自己推敲推敲还是能懂的。而且这道题细节也很多,膜拜了DaD3zZ大爷的代码完全弄懂。。
具体的一些实现细节就看看代码吧。(本来想详细的写写的,然而语文太差了,写了一半感觉发出来估计没什么人看得懂→_→)
${ans[i]}$表示公约数为${i}$的点对数目,${ANS[i]}$表示最大公约数为${i}$的点对数目。
细节
竟然1A了w(゚Д゚)w
代码
// uoj33 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #define LL long long #define inf 100000000 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxn=200010; int head[maxn],f[maxn],size[maxn],deep[maxn],vis[maxn],fa[maxn]; int Dargen,n,m,sum,cnt,maxd,maxD,block; LL NUM[maxn],num[maxn],Cnts[maxn],cnts[maxn],ans[maxn],ANS[maxn],F[500][500]; struct edge {int to,next;}e[maxn<<1]; void link(int u,int v) { e[++cnt]=(edge){v,head[u]};head[u]=cnt; e[++cnt]=(edge){u,head[v]};head[v]=cnt; } void caldargen(int x,int fa) { f[x]=0;size[x]=1; for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) { caldargen(e[i].to,x); size[x]+=size[e[i].to]; f[x]=max(f[x],size[e[i].to]); } f[x]=max(f[x],sum-size[x]); if (f[x]<f[Dargen]) Dargen=x; } void caldeep(int x,int fa) { cnts[deep[x]]++; for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) { deep[e[i].to]=deep[x]+1; caldeep(e[i].to,x); } } void work(int x) { vis[x]=1;maxd=maxD=0; for (int d,i=head[x];i;i=e[i].next) if (e[i].to!=fa[x] && !vis[e[i].to]) { deep[e[i].to]=1;caldeep(e[i].to,x); for (d=0;cnts[d+1];d++); maxd=max(maxd,d); for (int j=1;j<=d;j++) for (int k=j;k<=d;k+=j) num[j]+=cnts[k]; for (int j=1;j<=d;j++) ans[j]+=num[j]*NUM[j],ANS[j]+=cnts[j]; for (int j=1;j<=d;j++) Cnts[j]+=cnts[j],NUM[j]+=num[j],num[j]=cnts[j]=0; } Cnts[0]=1;int tt=0,D=1; for (int p=x,i=fa[x];i>=1 && !vis[i];p=i,i=fa[i],D++) { maxD=0; for (int d,j=head[i];j;j=e[j].next) if (e[j].to!=fa[i] && !vis[e[j].to] && e[j].to!=p) { deep[e[j].to]=1;caldeep(e[j].to,i); for (d=0;cnts[d+1];d++); maxD=max(maxD,d); } for (int k=1;k<=maxD;k++) for (int l=k;l<=maxD;l+=k) num[k]+=cnts[l]; tt=max(tt,maxD); for (int j=1;j<=min(block,maxD);j++) { if (F[j][D%j]==-1) { F[j][D%j]=0; for (int k=j-(D-1)%j-1;k<=maxd;k+=j) F[j][D%j]+=Cnts[k]; } ans[j]+=F[j][D%j]*num[j]; } for (int j=block+1;j<=maxD;j++) { LL tmp=0; for (int k=j-(D-1)%j-1;k<=maxd;k+=j) tmp+=Cnts[k]; ans[j]+=tmp*num[j]; } for (int i=1;i<=maxD;i++) num[i]=cnts[i]=0; } D--;int l=0,r=-1;LL tmp=0; for (int i=1;i<=D+maxd;i++) { tmp+=r+1<i ? Cnts[++r] : 0; tmp-=l<i-D ? Cnts[l++] : 0; ANS[i]+=tmp; } for (int i=1;i<=min(block,tt);i++) for (int j=0;j<=D;j++) F[i][j]=-1; for (int i=0;i<=maxd;i++) NUM[i]=Cnts[i]=0; for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) { sum=size[e[i].to];Dargen=0; caldargen(e[i].to,0); work(Dargen); } } int main() { scanf("%d",&n); block=(int)sqrt(n)+0.5; for (int i=2;i<=n;i++) { scanf("%d",&fa[i]); link(fa[i],i); } memset(F,-1,sizeof(F)); f[Dargen=0]=inf;sum=n; caldargen(1,0); work(Dargen); for (int i=n-1;i>=1;i--) for (int j=i+i;j<=n-1;j+=i) ans[i]-=ans[j]; for (int i=1;i<n;i++) printf("%lld ",ans[i]+ANS[i]); return 0; }