【模板】树链剖分

简介:

将一棵树划分成若干条链,用数据结构去维护每条链,复杂度为O(logN)。

功能:

1.将树从x到y结点最短路径上所有节点的值都加上z
2.求树从x到y结点最短路径上所有节点的值之和
3.将以x为根节点的子树内所有节点值都加上z
4.求以x为根节点的子树内所有节点值之和

模板:
P3384 【模板】轻重链剖分
#include<bits/stdc++.h>
#include<vector>
using namespace std;
#define ll long long
#define N 500005
vector<ll>G[N];
ll n,m,st,mod;
ll pos,x,y,z,cnt;
ll dep[N],siz[N],fa[N],top[N],id[N],son[N],base[N],a[N];

//-------------------------------------- 以下为线段树
typedef long long treetype;
struct point
{
	int l,r;
	treetype sum,lazy;
	void update(treetype v)
	{
		sum+=(r-l+1)*v;
		lazy+=v;
	}
}tree[400040];
inline void push_up(int id)
{
	tree[id].sum=tree[2*id].sum+tree[2*id+1].sum;
}
inline void build(int id,int l,int r)
{
	tree[id].l=l;
	tree[id].r=r;
	tree[id].sum=tree[id].lazy=0;
	if(l==r){tree[id].sum=base[l];}
	else
	{
		int mid=(l+r)>>1;
		build(id<<1,l,mid);
		build(id<<1|1,mid+1,r);
		push_up(id);
	}
}
inline void push_down(int id)
{
	treetype lazyval=tree[id].lazy;
	if(lazyval)
	{
		tree[2*id].update(lazyval);
		tree[2*id+1].update(lazyval);
		tree[id].lazy=0;
	}
}
inline void update(int id,int l,int r,treetype val)
{
	int L=tree[id].l,R=tree[id].r;
	if(l<=L&&R<=r){tree[id].update(val);}
	else
	{
		push_down(id);
		int mid=(L+R)>>1;
		if(mid>=l)update(id<<1,l,r,val);
		if(r>mid)update(id<<1|1,l,r,val);
		push_up(id);
	}
}
inline treetype query(int id,int l,int r)
{
	int L=tree[id].l,R=tree[id].r;
	if(l<=L&&R<=r){return tree[id].sum;}
	else
	{
		push_down(id);
		treetype ans=0;
		int mid=(L+R)>>1;
		if(mid>=l)ans+=query(id<<1,l,r);
		if(r>mid)ans+=query(id<<1|1,l,r);
		push_up(id);
		return ans;
	}
}
//-------------------------------------- 以上为线段树
void dfs1(int x,int f,int deep)//x当前节点,f父亲,deep深度
{
	dep[x]=deep;//标记点的深度
	siz[x]=1;//标记子树大小
	fa[x]=f;//标记点的父亲
	int maxson=-1;//记录重儿子的儿子数
	for(int i=0;i<G[x].size();i++)
	{
		if(G[x][i]==f)continue;//连边为父亲则退出
		dfs1(G[x][i],x,deep+1);//dfs儿子
		siz[x]+=siz[G[x][i]];//加上儿子的子树大小
		if(siz[G[x][i]]>maxson)
		{
			maxson=siz[G[x][i]];
			son[x]=G[x][i];//标记非叶子节点的重儿子编号
		}
	}
}
void dfs2(int x,int topf)//x当前节点,topf当前链的最顶端的节点
{
	id[x]=++cnt;//标记点的新编号
	base[id[x]]=a[x];/* 初始数据赋值 */
	top[x]=topf;//标记点所处链的顶端
	if(!son[x])return;
	dfs2(son[x],topf);//处理重儿子(优先)
	for(int i=0;i<G[x].size();i++)
	{
		if(G[x][i]==son[x]||G[x][i]==fa[x])continue;
		dfs2(G[x][i],G[x][i]);//处理轻儿子
	}
}
ll Modify_Range(int x,int y,ll k)//最短路径修改并返回最短路径权值和
{
	ll ans=0;
	k%=mod;//
	while(top[x]!=top[y])//当x,y处于不同链
	{
		if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点
		update(1,id[top[x]],id[x],k);//维护深度更深的链
		ans=(ans+query(1,id[top[x]],id[x]))%mod;//DYY:权值求和
		x=fa[top[x]];//维护完后找到链的父节点,继续更新直到x,y属于同一条链
	}
	if(dep[x]>dep[y])swap(x,y);
	update(1,id[x],id[y],k);//维护x到y的区间
	ans=(ans+query(1,id[x],id[y]))%mod;//DYY:权值求和
	return ans;
}
ll Modify_Tree(int x,ll k)//子树修改并返回子树权值和
{
	k%=mod;
	update(1,id[x],id[x]+siz[x]-1,k);//维护x所处的链(x到链尾)
	return query(1,id[x],id[x]+siz[x]-1)%mod;
}
int main()
{
    std::ios::sync_with_stdio(false);
    cin>>n>>m>>st>>mod;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<n;i++)
	{
		cin>>x>>y;
		G[x].push_back(y);
		G[y].push_back(x);
	}
	cnt=0;
	dfs1(st,0,1);
	dfs2(st,st);
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		cin>>pos;
		if(pos==1)
		{
			cin>>x>>y>>z;
			Modify_Range(x,y,z);
		}
		else if(pos==2)
		{
			cin>>x>>y;
			cout<<Modify_Range(x,y,0)<<endl;
		}
		else if(pos==3)
		{
			cin>>x>>z;
			Modify_Tree(x,z);
		}
		else
		{
			cin>>x;
			cout<<Modify_Tree(x,0)<<endl;
		}
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值