【uoj33】 UR #2—树上GCD

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\n",ans[i]+ANS[i]);
	return 0;
}

  

posted @ 2017-01-29 21:17  MashiroSky  阅读(386)  评论(3编辑  收藏  举报