点分树

变量

  • int rt\texttt{int rt}:重心。
  • int fat[x]\texttt{int fat[x]}:点 xx 在点分树上的父亲。
  • int sz[x]\texttt{int sz[x]}:点 xx 的子树大小。
  • int mx[x]\texttt{int mx[x]}:以 xx 为根时最大的子树的大小。
  • int vis[x]\texttt{int vis[x]}:点 xx 是否已经成为过分治中心。

函数

  • void findroot(int x,int fa,int S)\texttt{void findroot(int x,int fa,int S)}:当前连通块大小为 SS,节点 xx 的父亲为 fafa,在 xx 的子树内继续寻找当前连通块的重心。
  • void divide(int x,int S)\texttt{void divide(int x,int S)}:当前连通块大小为 SS,其分治中心为 xx,继续将其划分。
  • void build(int n)\texttt{void build(int n)}:建点分树,节点数为 nn
  • void change(int x,int v)\texttt{void change(int x,int v)}:将节点 xx 的权值增加 vv
  • int ask(int x,int k)\texttt{int ask(int x,int k)}:询问原树上到 xx 的距离不超过 kk 的节点的权值和。

代码

struct DFTree{
	int rt;
	int fat[N],sz[N],mx[N],vis[N];
	void findroot(int x,int fa,int S){
		sz[x]=1;mx[x]=0;
		for(int i=head[x];i;i=nxt[i]){
			int y=ver[i];
			if(y==fa||vis[y])continue;
			findroot(y,x,S);
			sz[x]+=sz[y];
			mx[x]=max(mx[x],sz[y]);
		}
		mx[x]=max(mx[x],S-sz[x]);
		if(!rt||mx[x]<mx[rt])rt=x;
	}
	void divide(int x,int S){
		vis[x]=1;int S_;
		tree.make(segrt1[x]);tree.make(segrt2[x]);
		for(int i=head[x];i;i=nxt[i]){
			int y=ver[i];
			if(vis[y])continue;
			rt=0;S_=(sz[x]<sz[y]?S-sz[x]:sz[y]);
			findroot(y,x,S_);fat[rt]=x;
			divide(rt,S_);
		}
	}
	void build(int n){
		rt=0;findroot(1,0,n);divide(rt,n);
	}
	void change(int x,int v){
		for(int y=x;y;y=fat[y]){
			tree.change(segrt1[y],dist(x,y),v);
			if(fat[y])
				tree.change(segrt2[y],dist(x,fat[y]),v);
		}
	}
	int ask(int x,int k){
		int ans=0;
		for(int y=x,z=0,d;y;y=fat[z=y]){
			if(dist(x,y)>k)continue;
			d=k-dist(x,y);
			ans+=tree.ask(segrt1[y],0,d);
			if(z)ans-=tree.ask(segrt2[z],0,d);
		}
		return ans;
	}
}dft;
posted @ 2024-02-27 15:00  luckydrawbox  阅读(11)  评论(0)    收藏  举报  来源