#点分治,根号分治#UOJ 33【UR #2】树上GCD

题目传送门


分析

如果 \(lca(x,y)=x\)\(y\),这种情况答案就是深度之差,直接特判。

否则 \(x,y\) 隶属于两个不同的子树,考虑点分治,那么 \(x,y\) 只可能同时是 \(root\) 的子孙

或一个是 \(root\) 的祖先的另一个子孙(包括本身)另一个隶属于 \(root\) 的子树。

由于 \(\gcd\) 比较难处理,转化为求倍数的答案最后再减掉。对于第一种情况比较简单,

考虑第二种情况,到 \(lca\) 的距离会改变,但是到 \(root\) 的距离不变,因此根据另一个点到 \(lca\) 的距离进行根号分治,

小于等于阈值的预处理,否则直接从余数开始不断加步长,都是一个根号,再加上之前的一种情况也就是 \(O(n\log^2n+n\sqrt n)\)


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=200011; typedef long long lll; struct node{int y,next;}e[N<<1];
int big[N],siz[N],SIZ,fat[N],root,n,as[N],v[N],dep[N],c[N],mx,MX; lll ans[N],sc[N],s[64][64];
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans; 
}
void print(lll ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
void dfs(int x,int fa){
	siz[x]=1,big[x]=0;
	for (int i=as[x];i;i=e[i].next)
	if (e[i].y!=fa&&!v[e[i].y]){
		dfs(e[i].y,x);
		siz[x]+=siz[e[i].y];
		big[x]=max(big[x],siz[e[i].y]);
	}
	big[x]=max(big[x],SIZ-siz[x]);
	if (big[x]<=big[root]) root=x;
}
void DFS(int x,int fa,int dep){
	++c[dep];
	if (dep>MX) MX=dep;
	for (int i=as[x];i;i=e[i].next)
	if (e[i].y!=fa&&!v[e[i].y])
	    DFS(e[i].y,x,dep+1);
}
void calc(int x){
	for (int i=as[x];i;i=e[i].next)
	if (!v[e[i].y]&&fat[e[i].y]==x){
		DFS(e[i].y,x,1);
		for (int j=1;j<=MX;++j)
		for (int k=j*2;k<=MX;k+=j)
		    c[j]+=c[k];
		for (int j=1;j<=MX;++j)
		    ans[j]+=c[j]*sc[j],sc[j]+=c[j],c[j]=0;
		if (MX>mx) mx=MX;
		MX=0;
	}
}
void CALC(int x,int rt){
	int las=x,D=1,bl=sqrt(mx)/7+1;
	for (int i=mx>>1;i;--i)
	for (int j=i*2;j<=mx;j+=i) sc[i]-=sc[j];
	++sc[0];
	for (int i=0;i<=mx;++i) if (sc[i])
	for (int j=1;j<=bl;++j) s[j][i%j]+=sc[i];
	for (int X=fat[x];X!=fat[rt];las=X,X=fat[X],++D){
		for (int i=as[X];i;i=e[i].next)
		    if (!v[e[i].y]&&fat[e[i].y]==X&&e[i].y!=las) DFS(e[i].y,X,1);
		for (int j=1;j<=MX;++j)
		for (int k=j*2;k<=MX;k+=j)
		    c[j]+=c[k];
		for (int j=1;j<=bl;++j) ans[j]+=c[j]*s[j][(j-D%j)%j],c[j]=0;
		for (int j=bl+1;j<=MX;++j)
		if (c[j]){
			lll sum=0;
			for (int k=(j-D%j)%j;k<=mx;k+=j) sum+=sc[k];
			ans[j]+=c[j]*sum,c[j]=0;
		}
		MX=0;
	}
	for (int j=1;j<=bl;++j)
	for (int i=0;i<j;++i) s[j][i]=0;
}
void dp(int x,int rt){
	v[x]=1,mx=0,calc(x);
	if (x!=rt) CALC(x,rt);
	for (int i=0;i<=mx;++i) sc[i]=0;
	for (int i=as[x];i;i=e[i].next)
	if (!v[e[i].y]){
		big[0]=SIZ=siz[e[i].y];
		dfs(e[i].y,root=0);
		dp(root,fat[x]==e[i].y?rt:e[i].y);
	}
}
int main(){
	n=iut();
	for (int i=2;i<=n;++i){
		fat[i]=iut(),dep[i]=dep[fat[i]]+1;
		e[i]=(node){i,as[fat[i]]},as[fat[i]]=i;
		e[i+n-1]=(node){fat[i],as[i]},as[i]=i+n-1;
	}
	big[0]=SIZ=n,dfs(1,root=0),dfs(root,0),dp(root,1);
	for (int i=n>>1;i;--i)
	for (int j=i*2;j<=n;j+=i) ans[i]-=ans[j];
	sc[1]=n-1;
	for (int i=2;i<=n;++i) --sc[dep[i]+1];
	for (int i=1;i<=n;++i) sc[i]+=sc[i-1];
	for (int i=1;i<n;++i) print(ans[i]+sc[i]),putchar(10);
	return 0;
}
posted @ 2025-06-23 01:14  lemondinosaur  阅读(12)  评论(0)    收藏  举报