UOJ33 【UR #2】树上GCD

感觉像是长剖?具体的,我们思考如何合并两个子树。

因为本质上 gcd 是属于高维前缀和的范畴的,我们使用 \(\text{Dirichlet}\) 后缀和来合并,然后再后缀差分一下。这样我们合并两个子树的复杂度就是 \(O((n+m)\log\log(n+m)\) 的,其中 \(n,m\) 是两棵子树的深度。

但是长剖需要我们快速在链头添加一个数,好像我们使用上面的做法不能轻易加一个值。


考虑了一波,我们对于轻链使用 \(\text{Dirichlet}\) 后缀和来维护,同时枚举其中每一个位置 \(k\) ,那么此时考虑暴力一点的做法就是在重儿子上枚举 \(k\) 的倍数,并查询即可。

考虑这里可以使用根号分治,如果 \(k>\sqrt{n}\) ,那么直接暴力做。如果 \(k\le \sqrt{n}\) ,我们此时可以预处理出重链上的点深度在模 \(k\) 的情况下各个余数的个数,这样我们就可以 \(O(1)\) 查询,同时加入是 \(O(\sqrt{n})\) 的。

感觉这道题目还需要一些 DSU on tree 的技巧,具体来说,就是由于我们那个桶同时只能存入一条重链的信息,所以我们要先处理轻链,然后将轻链从桶中删去,再处理重链(从重儿子处继承),最后再将轻儿子的东西合并上来。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,B=256;
int n;long long res[N],RES[N];
vector<int> pri;bool tag[N];
void init(int n){
	for(int i=2;i<=n;++i){
		if(!tag[i]) pri.push_back(i);
		for(int j=0;j<(int)pri.size()&&i*pri[j]<=n;++j){
			tag[i*pri[j]]=true;if(i%pri[j]==0) break;
		}
	}
}
struct Edge{int nxt,to;}e[N];int fir[N];
void add(int u,int v,int i){e[i]=(Edge){fir[u],v},fir[u]=i;}
struct Node{int fa,dep,dis,son;}tr[N];
void dfs(int u){
	tr[u].dep=tr[u].dis=tr[tr[u].fa].dep+1;
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==tr[u].fa) continue;
		tr[v].fa=u,dfs(v);
		if(tr[v].dis>tr[tr[u].son].dis)
			tr[u].son=v,tr[u].dis=tr[v].dis;
	}
	// printf("%d %d %d %d\n",u,tr[u].fa,tr[u].dep,tr[u].dis);
}
struct Bag{
	int siz;long long f[B][B],g[N];
	void add(int x,long long y){g[x]+=y;for(int i=1;i<siz;++i) f[i][x%i]+=y;}
	void clear(int x){g[x]=0;for(int i=1;i<siz;++i) f[i][x%i]=0;}
	long long query(int k,int x){return f[k][x%k];}
}bag;
vector<long long> h[N];
void Dirichlet(int u){
	int n=h[u].size();
	for(int i=0;i<(int)pri.size()&&pri[i]<n;++i){
		for(int j=(n-1)/pri[i];j>=1;--j) h[u][j]+=h[u][j*pri[i]];
	}
}
void IDirichlet(int u){
	int n=h[u].size();
	for(int i=0;i<(int)pri.size()&&pri[i]<n;++i){
		for(int j=1;j*pri[i]<n;++j) h[u][j]-=h[u][j*pri[i]];
	}
}
void DFS(int u){
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==tr[u].fa||v==tr[u].son) continue;
		DFS(v),h[v].push_back(0);
		for(int j=tr[v].dep;j<=tr[v].dis;++j)
			h[v].push_back(bag.g[j]),bag.clear(j);
	}
	if(tr[u].son) DFS(tr[u].son);
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==tr[u].fa||v==tr[u].son) continue;
		Dirichlet(v);
		for(int j=1;j<(int)h[v].size();++j){
			if(j<bag.siz)
				res[j]+=h[v][j]*bag.query(j,tr[u].dep);
			else for(int k=1;tr[u].dep+j*k<=tr[u].dis;++k)
				res[j]+=h[v][j]*bag.g[tr[u].dep+j*k];
		}
		IDirichlet(v);
		for(int j=0;j<(int)h[v].size();++j)
			bag.add(tr[u].dep+j,h[v][j]);
	}
	bag.add(tr[u].dep,1);
	// printf("%d\n",u);
	// for(int i=0;i<n;++i) printf("%lld ",bag.g[i]);
	// printf("\n");
	// for(int i=0;i<n;++i) printf("%lld ",res[i]);
	// printf("\n");
}
int main(){
	cin>>n,init(n),bag.siz=B;
	for(int i=2,fa;i<=n;++i) scanf("%d",&fa),add(fa,i,i);
	tr[0].dep=-1,dfs(1),DFS(1);
	for(int i=tr[1].dep;i<=tr[1].dis;++i)
		h[1].push_back(bag.g[i]),bag.clear(i);
	for(int i=1;i<(int)h[1].size();++i)
		RES[1]+=h[1][i],RES[i+1]-=h[1][i];
	for(int i=0;i<(int)pri.size()&&pri[i]<=tr[1].dis;++i){
		for(int j=1;j*pri[i]<tr[1].dis;++j) res[j]-=res[j*pri[i]];
	}
	long long sum=0;
	for(int i=1;i<n;++i) sum+=RES[i],printf("%lld\n",res[i]+sum);
	return 0;
}
posted @ 2021-12-29 18:54  Point_King  阅读(79)  评论(0编辑  收藏  举报