简介:
将一棵树划分成若干条链,用数据结构去维护每条链,复杂度为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;
}