落谷p3384 树链刨分 代码真长!

注释还算比较详细。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=100000+10;
const int maxm=100000+10;
struct Edge // 链式前向星存图 
{
	int before;
	int to;
}e[maxm<<1];
int n,m,r,mod,k,head[maxn];
int tim=0; // 时间戳 
int temp_val[maxn]; // 暂时记录当前节点权值  
int val[maxn]; // 记录当前dfs序结点的权值
int size[maxn];// 以当前结点为根的树的大小  
int top[maxn];  // 当前重链的链头 
int son[maxn]; // 记录当前节点的重儿子是谁 
int fa[maxn];// 当前节点的父节点
int dep[maxn]; // 当前结点的深度 
int ID[maxn]; // 结点新编号 即 dfs序 
int tree[maxn<<2],tag[maxn<<2]; // 存储线段树和懒惰标记 
void add(int u,int v)
{
	e[k].before=head[u];
	e[k].to=v;
	head[u]=k++;
}
void dfs1(int u,int f)
{
	fa[u]=f;  // 记录父节点 
	size[u]=1;   // 初始树大小为1 
	int max_son=-1;  // 用来求重儿子 
	for(int i=head[u];i!=-1;i=e[i].before)
	{
		int v=e[i].to;
		if(v==f)
		continue;  // 不能回去 
		dep[v]=dep[u]+1;
		dfs1(v,u);
		size[u]+=size[v];
		if(size[v]>max_son)
		{
			max_son=size[v];  
			son[u]=v; // 记录重儿子 
		}
	}
}
void dfs2(int u,int topf)
{
	ID[u]=++tim; // 节点的dfs序 
	val[tim]=temp_val[u]; // 将暂时记录的结点权值赋新的结点 
	top[u]=topf;   //   记录当前点的链头 
	if(!son[u])  
	return; // 如果没有儿子,那么返回 
	dfs2(son[u],topf);
	for(int i=head[u];i!=-1;i=e[i].before)
	{
		int v=e[i].to;
		if(v==fa[u]||v==son[u])
		continue;
		dfs2(v,v); // 从每一个轻儿子开始都是一条链 
	} 
}
void pushdown(int node,int lenL,int lenR)
{
	if(tag[node])
	{
		tag[node<<1]+=tag[node];
		tag[node<<1|1]+=tag[node];
		tree[node<<1]+=lenL*tag[node];
		tree[node<<1|1]+=lenR*tag[node];
		tag[node]=0;
	}
}
void creat(int node,int start,int end)
{
	if(start==end)
	{
		tree[node]=val[end];
		if(tree[node]>mod)
		{
			tree[node]%=mod;
		}		
		return; 
	}
	int mid=(start+end)>>1;
	creat(node<<1,start,mid);
	creat(node<<1|1,mid+1,end);
	tree[node]=(tree[node<<1]%mod+tree[node<<1|1]%mod)%mod;
}
void update(int node,int start,int end,int L,int R,int z) // 区间加 + z  
{
	if(L>end||R<start)
	{
		return;
	}
	if(L<=start&&R>=end)
	{
		tree[node]+=(end-start+1)*z;
		tag[node]+=z;
		return;
	}
	int mid=(start+end)>>1;
	pushdown(node,mid-start+1,end-mid);
	if(L<=mid)
	update(node<<1,start,mid,L,R,z);
	if(R>=mid+1)
	update(node<<1|1,mid+1,end,L,R,z);
	tree[node]=(tree[node<<1]%mod+tree[node<<1|1]%mod)%mod;
}

int query(int node,int start,int end,int L,int R) // L, R  为查询区间
{
	if(start>R||end<L)
	{
		return 0;
	}
	if(start>=L&&end<=R)
	{
		return tree[node];
	}
	else
	{
		int mid=(start+end)>>1;
		pushdown(node,mid-start+1,end-mid);
		int lans=0;
		int rans=0;
		if(mid>=L)
		lans=query(node<<1,start,mid,L,R);
		if(mid+1<=R)
		rans=query(node<<1|1,mid+1,end,L,R);
		return (lans+rans)%mod;
	}
}
void update_range(int x,int y,int z)  
{
	while(top[x]!=top[y])  // 两个点还不在一个条链上 
	{
		if(dep[top[x]]<dep[top[y]]) // 深度大的点先向上跳跃 
		{
			swap(x,y);
		}
		update(1,1,n,ID[top[x]],ID[x],z);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])  // 在一条链上之后 深度小的结点的dfs序小 
	swap(x,y);
	update(1,1,n,ID[x],ID[y],z);
}
int query_range(int x,int y)
{
	int ans=0;
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])
		swap(x,y);
		ans=(ans+query(1,1,n,ID[top[x]],ID[x]))%mod;
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])
	{
		swap(x,y);
	}
	ans=(ans%mod+query(1,1,n,ID[x],ID[y])%mod)%mod;
	return ans;
}
int main()
{
	#ifdef ONLINE_JUDGE
	#else 
	freopen("in.txt","r",stdin);
	#endif
	int mode,a,b,x,y,z;
	memset(head,-1,sizeof head);
	scanf("%d %d %d %d",&n,&m,&r,&mod);
	for(int i=1;i<=n;i++)
	scanf("%d",&temp_val[i]);
	for(int i=1;i<n;i++)
	{
		scanf("%d %d",&a,&b);
		add(a,b);
		add(b,a);	
	}
	// 第一次dfs得到每个点的深度和重儿子
	// 以及每个点的父节点 和 以当前节点作为根的树的结点个数 
	// 第二次dfs给每个点重新标号,每个点的标号为他们的dfs序 
	dep[r]=1;	// 根节点深度为 1 
	dfs1(r,-1); // 根节点无父节点  
	dfs2(r,r);  // 从根节点开始第二次dfs  
	creat(1,1,n);
	for(int i=0;i<m;i++)
	{
		scanf("%d",&mode);
		if(mode==1)
		{
			scanf("%d %d %d",&x,&y,&z); // 将树上x到y上最短路径 上的所有点的值加上z 
			z=z%mod;
			update_range(x,y,z);
		}
		if(mode==2)
		{
			scanf("%d %d",&x,&y);	// 求树上x到y上最短路径上的所有点的值的和 
			printf("%d\n",query_range(x,y));
		}
		if(mode==3)
		{
			scanf("%d %d",&x,&z); // 以x作为根节点的子树上的所有结点值+z 
			z%=mod;
			update(1,1,n,ID[x],ID[x]+size[x]-1,z); 
		}
		if(mode==4)    			
		{
			scanf("%d",&x); 	//求以x作为根节点的子树的所有点权值和 
			printf("%d\n",query(1,1,n,ID[x],ID[x]+size[x]-1)%mod);
		}
	}	
	return 0;	
} 
发布了51 篇原创文章 · 获赞 21 · 访问量 3080

猜你喜欢

转载自blog.csdn.net/qq_44115065/article/details/103227166