树链剖分模板【洛谷P3384】

题目见洛谷。

这里有一个讲解非常好且详细的博客:http://www.cnblogs.com/chinhhh/p/7965433.html,几乎可以包懂包会写。

下面就是单纯贴一下根据上面博客思路写的AC的代码,加了点注释。其实理解DFS序之后就很好理解了。

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=100005;
int np=0,sum[MAXN*2],add[MAXN*2],lc[MAXN*2],rc[MAXN*2];
int np2=0,last[MAXN],a[MAXN],b[MAXN],dep[MAXN],fa[MAXN],size[MAXN],dfs_pos[MAXN],top_node[MAXN],big_ch[MAXN],dfs_clock=0;
int N,M,R,P,rt=0;
struct edge{int to,pre;}E[MAXN*2];
//--------------------------------------输入输出优化 
char c;
void scan(int &x)
{
	for(c=getchar();c<'0'||c>'9';c=getchar());
	for(x=0;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
}

char num[20];int ct;
void print(int x)
{
	ct=0; if(!x) num[ct++]='0';
	while(x) num[ct++]=x%10+'0',x/=10;
	while(ct--) putchar(num[ct]);
	putchar('\n');
}
//---------------------------------------加边 

void addedge(int u,int v)
{
	E[++np2]=(edge){v,last[u]};
	last[u]=np2;
}

//------------------------------------------线段树的基本操作 
void pushup(int now) {sum[now]=(sum[lc[now]]+sum[rc[now]])%P;}

void build(int &now,int L,int R)
{
	now=++np;
	if(L==R)
	{
		sum[now]=b[L];
		return;
	}
	int mid=(L+R)/2;
	build(lc[now],L,mid);
	build(rc[now],mid+1,R);
	pushup(now);
}

void f(int now,int L,int R,int Add) {sum[now]=(sum[now]+Add*(R-L+1)%P)%P; add[now]=(add[now]+Add)%P;}

void pushdown(int now,int L,int R)
{
	if(!add[now]) return;
	int mid=(L+R)/2;
	f(lc[now],L,mid,add[now]);
	f(rc[now],mid+1,R,add[now]);
	add[now]=0;
}

void update(int now,int L,int R,int x,int y,int k)
{
	if(x<=L&&R<=y)
	{
		sum[now]=(sum[now]+k*(R-L+1))%P;
		add[now]=(add[now]+k)%P;
		return;
	}
	pushdown(now,L,R);
	int mid=(L+R)/2;
	if(x<=mid) update(lc[now],L,mid,x,y,k);
	if(mid<y) update(rc[now],mid+1,R,x,y,k);
	pushup(now);
}

int ques(int now,int L,int R,int x,int y)
{
	if(x<=L&&R<=y) return sum[now];
	pushdown(now,L,R);
	int mid=(L+R)/2,ret=0;
	if(x<=mid) ret=(ret+ques(lc[now],L,mid,x,y)%P)%P;
	if(mid<y) ret=(ret+ques(rc[now],mid+1,R,x,y)%P)%P;
	pushup(now); return ret;
}

//---------------------------------树链剖分两次DFS 

void DFS1(int i,int f,int d)
{
	dep[i]=d;size[i]=1;fa[i]=f;
	for(int p=last[i];p;p=E[p].pre)
	{
		int j=E[p].to;
		if(j==f) continue;
		DFS1(j,i,d+1);
		size[i]+=size[j];
		if(size[j]>size[big_ch[i]]) big_ch[i]=j;
	}
}

void DFS2(int i,int top)
{
	dfs_pos[i]=++dfs_clock;   //DFS序 
	top_node[i]=top; b[dfs_clock]=a[i];  //记录i所在链的顶部节点,把i的值转化到DFS序上 
	if(!big_ch[i]) return;
	DFS2(big_ch[i],top); //先构造重链 
	for(int p=last[i];p;p=E[p].pre)
	{
		int j=E[p].to;
		if(j==fa[i]||j==big_ch[i]) continue;
		DFS2(j,j);  //任何一个轻节点都有从自己出发的链 
	}
}
//---------------------------------------树剖特有操作 

void uprange(int x,int y,int t)  //修改两点间最短路上的边 
{
	t%=P;
	while(top_node[x]!=top_node[y])  //如果x和y还没走到一条链上 
	{
		if(dep[top_node[x]]<dep[top_node[y]]) swap(x,y);  //类似LCA,尽量使x,y深度接近 
		update(rt,1,N,dfs_pos[top_node[x]],dfs_pos[x],t);  //连续的DFS序 
		x=fa[top_node[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	update(rt,1,N,dfs_pos[x],dfs_pos[y],t);
}

int qrange(int x,int y)  //查询两点间最短路距离,与上一样 
{
	int ans=0;
	while(top_node[x]!=top_node[y])
	{
		if(dep[top_node[x]]<dep[top_node[y]]) swap(x,y);
		ans=(ans+ques(rt,1,N,dfs_pos[top_node[x]],dfs_pos[x])%P)%P;
		x=fa[top_node[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	return (ans+ques(rt,1,N,dfs_pos[x],dfs_pos[y])%P)%P;
}

void uptree(int root,int t) {update(rt,1,N,dfs_pos[root],dfs_pos[root]+size[root]-1,t);}  //对一棵树所有子节点修改 

int qtree(int root) { return ques(rt,1,N,dfs_pos[root],dfs_pos[root]+size[root]-1); }  //查询一棵树所有子节点值之和 

int main()
{
	int x,y,z,i;
	scan(N);scan(M);scan(R);scan(P);
	for(i=1;i<=N;i++) scan(a[i]); 
	for(i=1;i<N;i++)
	{
		scan(x);scan(y);
		addedge(x,y);
		addedge(y,x);
	}
	DFS1(R,0,1); DFS2(R,R); build(rt,1,N);
	while(M--)
	{
		scan(i);
		if(i==1)
		{
			scan(x);scan(y);scan(z);
			uprange(x,y,z);
		}
		else if(i==2)
		{
			scan(x);scan(y);
			print(qrange(x,y));
		}
		else if(i==3)
		{
			scan(x);scan(z);
			uptree(x,z);
		}
		else
		{
			scan(x);
			print(qtree(x));
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/81437586