【洛谷】世界树-虚树/树形DP

传送门:洛谷-世界树


题意

世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为1。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;
例如,若聚居地a和b之间有道路,b和c之间有道路,因为每条道路长度为1而且又不可能出现环,所以a与c之间的距离为2。出于对公平的考虑,第i年,世界树的国王需要授权m[i]个种族的聚居地为临时议事处。对于某个种族x(x为种族的编号),如果距离该种族最近的临时议事处为y(y为议事处所在聚居地的编号),则种族x将接受y议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则y为其中编号最小的临时议事处)。
现在国王想知道,在q年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。


数据范围

N<=300000, q<=300000,m[1]+m[2]+…+m[q]<=300000


题解

先建一棵树模拟一下,就会发现对于每个点,如果它的子树里都没有议事处,那么肯定归它管,对于它上面的,找到距离恰为和祖先中第一个议事处距离的一半的,判一下标号大小就好了。
那么结合m的提示,我们对于每一次询问建一颗虚树。


代码

#include
using namespace std;
const int N=3e5+10;
int n,x,y,h[N],tot,Q,m,dfn,flor;
int head[N],to[N<<1],nxt[N<<1],a[N],b[N],c[N],top;
int d[N],ans[N],f[N][20],sz[N],rem[N];
int id[N],bel[N],s[N],mx; 

inline int read()
{
	char ch=getchar();int x=0,t=1;
	while(ch<'0' || ch>'9') {if(ch=='-') t=-1;ch=getchar();}
	while(ch>='0' && ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*t;
}

inline bool cmp(int x,int y)
{
	return id[x]d[y]) swap(x,y);
   int t=d[y]-d[x];
   for(register int i=0;(1LL<=0;i--){
   	  if(f[x][i]!=f[y][i]){
   	  	 x=f[x][i];y=f[y][i];
   	  }
   }
   if(x!=y){
   	return f[x][0];
   }else{
   	return x;
   }
}

inline int dis(int x,int y)
{
	return (d[x]+d[y]-2*(d[lca(x,y)])); 
}

inline void lk(int u,int v)
{
	to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}

inline void dfs(int x,int fa)
{
	int e;sz[x]=1;id[x]=++dfn;
	for(register int i=1;(1LL<=0;i--){
	 	if(d[f[x][i]]>d[a]){
	 		x=f[x][i];
	 	}
	 }	
	 rem[a]-=sz[x];
	 if(bel[a]==bel[b]){
	 	ans[bel[a]]+=sz[x]-sz[b];
	 }else{
	 	int e;
	 	for(register int i=flor;i>=0;i--){
	 		e=f[ct][i];
			if(d[e]<=d[a]) continue;
	 		int t1=dis(bel[a],e),t2=dis(bel[b],e);
	 		if(((t1==t2)&&(bel[b]t2)) ct=e;
	 	}
	 	ans[bel[a]]+=(sz[x]-sz[ct]);
	 	ans[bel[b]]+=(sz[ct]-sz[b]);
	 }
}

int main(){
	n=read();
	for(register int i=1;i0){
			  p=lca(s[top],t);
			  if(top>1 && d[p]1){lk(s[top-1],s[top]);top--;}
		   df(1);dff(1);
		   for(register int i=1;i<=dfn;i++){
		   	for(register int j=head[c[i]];j;j=nxt[j]){
		   		solve(c[i],to[j]);
		   	}
		   }
		   for(register int i=1;i<=dfn;i++) 
		     ans[bel[c[i]]]+=rem[c[i]];
		   for(register int i=1;i<=m;i++) printf("%d ",ans[b[i]]);
		   printf("\n");
		   for(register int i=1;i<=dfn;i++){
		   	  ans[c[i]]=bel[c[i]]=rem[c[i]]=head[c[i]]=0;
		   }
		} 
		return 0;
}

ps:代码参照hzwer

你可能感兴趣的:(树形DP,虚树)