On tree chain subdivision

Posted by sonic_2k_uk on Sat, 19 Feb 2022 22:39:54 +0100

Tree chain subdivision

Template question

As the name suggests, it is to express the shape of the tree with a linear linked list.
Then the first thing is to build a tree.

ll siz[N],dep[N],son[N],fa[N];//Son is the coordinate of the great son whose ancestor is x 
void build_tree(int now)
{
	siz[now]=1;//Size of initialization tree
	for(int i=he[now];i;i=nxt[i])
	{
		int tar=to[i];
		if(tar==fa[now]) continue;
		fa[tar]=now,dep[tar]=dep[now]+1;
		build_tree(tar);
		siz[now]+=siz[tar];
		if(siz[tar]>siz[son[now]]) son[now]=tar;
	 } 
}

Well, first of all, in addition to normal tree building, we also need to make statistics on the size of the subtree with each node as the root, so as to facilitate the later determination of valuing sons and neglecting sons. So what is valuing sons? For example, I now have a node 2, so I have two son nodes 4 and 5, Then the size of the subtree with 4 and 5 as nodes is 3 and 2 respectively. Then, because the subtree with 4 is large, we say that 4 is the heavy son of 3, connected with a heavy edge. The son array in the code records the id of the heavy son with the current node as the parent node.

Then the next step is to use the array to divide the chain through the mark of the heavy son just now.

ll past[N]/*A mapping of dfs order to point coordinates*/,dfs[N]/*dfs order of the point*/,top[N],tot,ctr[N]; 
void remark(int poi,int tp)//The point and the top of the heavy chain where the current point is located 
{
    top[poi]=tp;
 	dfs[poi]=++tot;
 	past[tot]=poi;
 	if(son[poi]) remark(son[poi],tp);//Give priority to the heavy chain
	for(int i=he[poi];i;i=nxt[i])
	{
		int tar=to[i];
		if(tar!=son[poi]&&tar!=fa[poi]) remark(tar,tar);//light chain
	 } 
 	ctr[poi]=tot;//The range of subtree is dfs[poi]~ctr[poi], which records the maximum dfs order of subtree with poi as root 
}

The ctr array here records the maximum dfs continuation of each subtree, which is actually the length of each chain partition.

Then, in order to facilitate the query of these chains, we need to use some other data structures to maintain these chains. Here, the small editor uses a segment tree.

int len;//For dfs order records of line segment tree, 
struct node
{
	int l,r,idl,idr/*On the number of left and right sons of len's mapping*/;
	ll c,late;//c record the weight of heavy chain 
}tree[N];
void line_tree(int x,int y)
{
	++len;
	tree[len].l=x;
	tree[len].r=y;
	tree[len].late=0;
	if(x==y)
	{
	  tree[len].idl=tree[len].idr=-1;
	  tree[len].c=val[past[x]];//Number mapped back to the original main tree	
	}		
	else
	{
		int now=len,mid=(x+y)>>1;
		tree[now].idl=len+1,line_tree(x,mid);
		tree[now].idr=len+1,line_tree(mid+1,y);
		tree[now].c=tree[tree[now].idl].c+tree[tree[now].idr].c;
	} 
}
void sent(int x)//For the download operation of the line segment tree, the late part is lazy 
{	
	if(tree[x].late)
	{
		tree[x].c+=tree[x].late*(tree[x].r-tree[x].l+1);
		int q=tree[x].idl,p=tree[x].idr;
		if(q!=-1) tree[q].late+=tree[x].late,tree[p].late+=tree[x].late;//If there is a son after this node, the download operation is performed 
		tree[x].late=0;
	}	
} 

Here, we use the structure as the tree unit, which is more convenient for recording. In fact, it is mapped through the range of each chain in the tree. In this way, through the division of heavy chain and light chain, the time complexity of logn can be achieved for each query.

In fact, the query can be realized here. How to change the weight in the tree.

void change(int now,int x,int y,int z)
{
	if(tree[now].l==x&&tree[now].r==y)
	{ 
		tree[now].late+=z;
		sent(now);
		return; 
	}	
	sent(now);
	int q=tree[now].idl,p=tree[now].idr;
	int mid=(tree[now].l+tree[now].r)>>1;
	if(y<=mid) change(q,x,y,z);
	else if(x>mid) change(p,x,y,z);
	else change(q,x,mid,z),change(p,mid+1,y,z);
	tree[now].c+=(y-x+1)*z;
} 

In fact, here is to modify the length of the chain to facilitate the weight transmission, and modify the sum value in the area.

Next, we will query.

void lca(int x,int y,int z)
{
	while(top[x]!=top[y])//Not on one chain 
	{
		if(dep[top[x]]>dep[top[y]]) swap(x,y);//The default value of deep depth is y priority to skip the vertex depth of heavy chain
		change(1,dfs[top[y]],dfs[y],z);//Start from the first floor 
		y=fa[top[y]]; 
	}	
	if(dep[x]>dep[y]) swap(x,y);//exchange 
	change(1,dfs[x],dfs[y],z);//When in the same heavy chain, update again 
} 

lca is used here. Since the id of the original tree is given here, we need to use the id of the mapped segment tree, so we need to use dfs to find it from the first layer of the segment tree.

void sum(int x,int y)//Weight sum of statistics x~y
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]]) swap(x,y);
		ans=(ans+get_sum(1,dfs[top[y]],dfs[y])%p)%p;
		y=fa[top[y]];	
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=get_sum(1,dfs[x],dfs[y]);//Already on the same heavy chain 
	ks(ans%p),putchar('\n');	 
} 

Complete code

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const ll N=2e5+11;
ll n,m,r,p;
int opt,x,y,z;
ll cnt,val[N],to[N],he[N],nxt[N];
ll qread()
{
	ll xx=0;
	char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') 
	{
		xx=(xx<<1)+(xx<<3)+c-'0';
		c=getchar();	
	}	
	return xx;
}
void ks(ll num)
{
    if(num>9) ks(num/10ll);
	putchar(num%10+'0');	
}
void add(int u,int v)
{
	++cnt;
	nxt[cnt]=he[u];
	he[u]=cnt;
	to[cnt]=v;
}
ll siz[N],dep[N],son[N],fa[N];//Son is the coordinate of the great son whose ancestor is x 
void build_tree(int now)
{
	siz[now]=1;//Size of initialization tree
	for(int i=he[now];i;i=nxt[i])
	{
		int tar=to[i];
		if(tar==fa[now]) continue;
		fa[tar]=now,dep[tar]=dep[now]+1;
		build_tree(tar);
		siz[now]+=siz[tar];
		if(siz[tar]>siz[son[now]]) son[now]=tar;
	 } 
}
ll past[N]/*A mapping of dfs order to point coordinates*/,dfs[N]/*dfs order of the point*/,top[N],tot,ctr[N]; 
void remark(int poi,int tp)//The point and the top of the heavy chain where the current point is located 
{
    top[poi]=tp;
 	dfs[poi]=++tot;
 	past[tot]=poi;
 	if(son[poi]) remark(son[poi],tp);//Give priority to the heavy chain
	for(int i=he[poi];i;i=nxt[i])
	{
		int tar=to[i];
		if(tar!=son[poi]&&tar!=fa[poi]) remark(tar,tar);//light chain
	 } 
 	ctr[poi]=tot;//The range of subtree is dfs[poi]~ctr[poi], which records the maximum dfs order of subtree with poi as root 
}
int len;//For dfs order records of line segment tree, 
struct node
{
	int l,r,idl,idr/*On the number of left and right sons of len's mapping*/;
	ll c,late;//c record the weight of heavy chain 
}tree[N];
void line_tree(int x,int y)
{
	++len;
	tree[len].l=x;
	tree[len].r=y;
	tree[len].late=0;
	if(x==y)
	{
	  tree[len].idl=tree[len].idr=-1;
	  tree[len].c=val[past[x]];//Number mapped back to the original main tree	
	}		
	else
	{
		int now=len,mid=(x+y)>>1;
		tree[now].idl=len+1,line_tree(x,mid);
		tree[now].idr=len+1,line_tree(mid+1,y);
		tree[now].c=tree[tree[now].idl].c+tree[tree[now].idr].c;
	} 
}
void sent(int x)//For the download operation of the line segment tree, the late part is lazy 
{	
	if(tree[x].late)
	{
		tree[x].c+=tree[x].late*(tree[x].r-tree[x].l+1);
		int q=tree[x].idl,p=tree[x].idr;
		if(q!=-1) tree[q].late+=tree[x].late,tree[p].late+=tree[x].late;//If there is a son after this node, the download operation is performed 
		tree[x].late=0;
	}	
} 
void change(int now,int x,int y,int z)
{
	if(tree[now].l==x&&tree[now].r==y)
	{ 
		tree[now].late+=z;
		sent(now);
		return; 
	}	
	sent(now);
	int q=tree[now].idl,p=tree[now].idr;
	int mid=tree[now].l+tree[now].r>>1;
	if(y<=mid) change(q,x,y,z);
	else if(x>mid) change(p,x,y,z);
	else change(q,x,mid,z),change(p,mid+1,y,z);
	tree[now].c+=(y-x+1)*z;
} 
ll get_sum(int now,int x,int y)
{
	sent(now);//Download before accessing, otherwise it may cause data loss
	if(tree[now].l==x&&tree[now].r==y) return tree[now].c;
	int q=tree[now].idl,p=tree[now].idr;
	int mid=(tree[now].l+tree[now].r)>>1;
	if(y<=mid) return get_sum(q,x,y);
	else if(x>mid) return get_sum(p,x,y);
	else return get_sum(q,x,mid)+get_sum(p,mid+1,y);
}
void lca(int x,int y,int z)
{
	while(top[x]!=top[y])//Two points are not on a heavy chain 
	{
		if(dep[top[x]]>dep[top[y]]) swap(x,y);//The default value of deep depth is y priority to skip the vertex depth of heavy chain
		change(1,dfs[top[y]],dfs[y],z);//Start from the first floor 
		y=fa[top[y]]; 
	}	
	if(dep[x]>dep[y]) swap(x,y);//exchange 
	change(1,dfs[x],dfs[y],z);//When in the same heavy chain, update again 
} 
void sum(int x,int y)//Weight sum of statistics x~y
{
	ll ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]>dep[top[y]]) swap(x,y);
		ans=(ans+get_sum(1,dfs[top[y]],dfs[y])%p)%p;
		y=fa[top[y]];	
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans+=get_sum(1,dfs[x],dfs[y]);//Already on the same heavy chain 
	ks(ans%p),putchar('\n');	 
} 
void get_sum_sontree(int x)
{
	ks(get_sum(1,dfs[x],ctr[x])%p),putchar('\n'); 
}
void change_sontree(int x,int y)
{
	change(1,dfs[x],ctr[x],y);
}
int main()
{
	n=qread(),m=qread(),r=qread(),p=qread();
	for(int i=1;i<=n;++i) val[i]=qread();
	for(int i=1,x,y;i<=n-1;++i) x=qread(),y=qread(),add(x,y),add(y,x);
    build_tree(r);//Jianzhu tree 
    remark(r,r);//Record heavy chain 
    line_tree(1,tot);//Maintain heavy chain with segment tree 
	while(m--)
	{
		opt=qread();
		if(opt==1)
		{
			x=qread(),y=qread(),z=qread();
			lca(x,y,z);	
		}
		else if(opt==2)
		{
			x=qread(),y=qread();
			sum(x,y);
		}
		else if(opt==3)
		{
			x=qread(),z=qread();
			change_sontree(x,z);
		}
		else
		{
			x=qread();
			get_sum_sontree(x);
		}
	}
	return 0;
}

Topics: Algorithm Luogu