概念;
重儿子:一个节点的子节点中,以那个子节点为根,它的子树有最多的子节点,这个节点就是重儿子
轻儿子:非重儿子
重链:连接一条重儿子的链
轻链:一个轻儿子就是一个轻链
举个栗子:
图片引用于:https://www.cnblogs.com/chinhhh/p/7965433.html#firstt
链式前向星存图:
//链式前向星
struct nod{
int next,to;
}ee[maxn*3];
int head[maxn];
void add(int a,int b)
{
ee[cnt].to=b;
ee[cnt].next=head[a];
head[a] = cnt;
cnt++;
}
dfs1()代码:
dfs1用来求出每个节点的深度dep数组
每个节点的父亲fa数组
每个节点的子节点个数(包括他自己)siz数组
每个节点的重儿子的编号son数组
void dfs1(int x,int f,int deep) //x为当前节点,f为父亲节点,deep为深度
{
dep[x] = deep;
fa[x] = f;
siz[x] = 1;
int maxson=-1;
for(int i=head[x];~i;i=ee[i].next){
int y=ee[i].to;
if(y == f) //如果是父亲则continue
continue;
dfs1(y,x,deep+1); //y是x的子节点
siz[x]+=siz[y];
if(siz[y] > maxson) //找出重儿子并标记
son[x]=y,maxson=siz[y]; //如果有多个重儿子,随机标记一个
}
}
dfs2()
因为每次都先找重儿子,所以重链的编号是连续的,轻链在重链之后,每个子树的编号也是连续的
void dfs2(int x,int topf) //x是当前节点,topf是链的顶端
{
id[x] = cc; //cc从1开始,id存每个节点的新编号
wt[cc] = a[x]; //wt存的是新编号对应的点的权值
cc++;
top[x] = topf; //top存每个链的顶端
if(!son[x]) //先去处理重儿子,在处理轻儿子
return ;
dfs2(son[x],topf);
for(int i=head[x];~i;i=ee[i].next){
int y=ee[i].to;
if(y == son[x] || y == fa[x]) //如果是重儿子或者父亲节点则continue
continue;
dfs2(y,y); //对于每一个轻儿子都有自己的一条链
}
}
如图:
图片引用于:https://www.cnblogs.com/chinhhh/p/7965433.html#firstt
处理任意两点间的路径:
设所在的链深度更深的那个点是x
当x和y不在同一条链上时,
ans不断加上x到x所在链的顶端点权
x跳到x所在的链的顶部的父亲节点
在同一条链上以后,加上两点之间的权值
举个栗子:
图片引用于:https://www.cnblogs.com/chinhhh/p/7965433.html#firstt
更新代码:
void updrange(int x,int y,ll k)
{
k %=pp;
while(top[x] != top[y]){ //不在同一条链上时
if(dep[top[x]] < dep[top[y]])
swap(x,y);
change(1,id[top[x]],id[x],k); //更改x到x所在的链顶
x = fa[top[x]]; //x跳到另一条链上去
}
if(dep[x] > dep[y])
swap(x,y);
change(1,id[x],id[y],k); //当在同一条链上时加上区间和
}
求和代码:
void qrange(int x,int y)
{
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])
swap(x,y);
ask(1,id[top[x]],id[x]);
x = fa[top[x]];
}
if(dep[x] > dep[y])
swap(x,y);
ask(1,id[x],id[y]);
return ;
}
更改某一点的所有子节点:
void updson(int x,ll z){
change(1,id[x],id[x]+siz[x]-1,z); //子树的右端点为id[x]+siz[x]-1
}
求某一点的所有子节点的和:
void qson(int x)
{
ask(1,id[x],id[x]+siz[x]-1);
return ;
}
更改、查询过程用到线段树!
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
#define memset(a,b) memset(a,b,sizeof(a))
#define ll long long
#define inf 0x3f3f3f3f
#define mod 1000000007
const int maxn=1e6+10;
//线段树
struct node{
ll lazy,pre;
int l,r;
}edge[maxn*4];
//链式前向星
struct nod{
int next,to;
}ee[maxn*3];
int head[maxn];
int dep[maxn]; //深度数组
int fa[maxn]; //父亲数组
int siz[maxn]; //子树大小数组
int son[maxn]; //重儿子编号数组
int top[maxn]; //链顶端的点的编号
ll a[maxn]; //初始值数组
int id[maxn]; //新编号数组
ll wt[maxn]; //新编号的权值数组
ll pp;
int cnt=1;
int cc=1;
ll ans;
void add(int a,int b)
{
ee[cnt].to=b;
ee[cnt].next=head[a];
head[a] = cnt;
cnt++;
}
//--------------------------------------------------以下为线段树
void build(int p,int l,int r)
{
edge[p].l=l,edge[p].r=r;
if(l == r){
edge[p].pre=wt[l];
edge[p].pre = edge[p].pre % pp;
return ;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
edge[p].pre=(edge[p<<1].pre+edge[p<<1|1].pre)%pp;
}
void spread(int p)
{
if(edge[p].lazy){
edge[p<<1].lazy+=edge[p].lazy;
edge[p<<1].lazy%=pp;
edge[p<<1|1].lazy+=edge[p].lazy;
edge[p<<1|1].lazy%=pp;
edge[p<<1].pre+=edge[p].lazy*(ll)(edge[p<<1].r-edge[p<<1].l+1);
edge[p<<1].pre%=pp;
edge[p<<1|1].pre+=edge[p].lazy*(ll)(edge[p<<1|1].r-edge[p<<1|1].l+1);
edge[p<<1].pre%=pp;
edge[p].lazy=0;
}
}
void change(int p,int x,int y,ll z)
{
if(edge[p].l >= x && edge[p].r <= y){
edge[p].lazy+=z;
edge[p].lazy%=pp;
edge[p].pre += z*(ll)(edge[p].r-edge[p].l+1);
edge[p].pre%=pp;
return ;
}
spread(p);
int mid=(edge[p].l+edge[p].r)>>1;
if(x <= mid)
change(p<<1,x,y,z);
if(mid <y)
change(p<<1|1,x,y,z);
edge[p].pre=edge[p<<1].pre+edge[p<<1|1].pre;
edge[p].pre%=pp;
return ;
}
void ask(int p,int x,int y)
{
if(edge[p].l >= x && edge[p].r <=y){
ans+=edge[p].pre;
ans %=pp;
return ;
}
spread(p);
int mid = (edge[p].l+edge[p].r)>>1;
if(x <= mid)
ask(p<<1,x,y);
if(mid < y)
ask(p<<1|1,x,y);
}
//--------------------------------------------------以上为线段树
void dfs1(int x,int f,int deep)
{
dep[x] = deep;
fa[x] = f;
siz[x] = 1;
int maxson=-1;
for(int i=head[x];~i;i=ee[i].next){
int y=ee[i].to;
if(y == f) //如果是父亲则continue
continue;
dfs1(y,x,deep+1);
siz[x]+=siz[y];
if(siz[y] > maxson) //找出重儿子并标记
son[x]=y,maxson=siz[y];
}
}
void dfs2(int x,int topf)
{
id[x] = cc;
wt[cc] = a[x];
cc++;
top[x] = topf;
if(!son[x]) //先去处理重儿子,在处理轻儿子
return ;
dfs2(son[x],topf);
for(int i=head[x];~i;i=ee[i].next){
int y=ee[i].to;
if(y == son[x] || y == fa[x])
continue;
dfs2(y,y); //对于每一个轻儿子都有自己的一条链
}
}
void updrange(int x,int y,ll k)
{
k %=pp;
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])
swap(x,y);
change(1,id[top[x]],id[x],k); //更改x到x所在的链顶
x = fa[top[x]]; //x跳到另一条链上去
}
if(dep[x] > dep[y])
swap(x,y);
change(1,id[x],id[y],k); //当在同一条链上时加上区间和
}
void qrange(int x,int y)
{
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])
swap(x,y);
ask(1,id[top[x]],id[x]);
x = fa[top[x]];
}
if(dep[x] > dep[y])
swap(x,y);
ask(1,id[x],id[y]);
return ;
}
void updson(int x,ll z){
change(1,id[x],id[x]+siz[x]-1,z);
}
void qson(int x)
{
ask(1,id[x],id[x]+siz[x]-1);
return ;
}
int main()
{
int p,q,n,m,r,op;
int x,y;
ll z;
memset(head,-1);
scanf("%d %d %d %lld",&n,&m,&r,&pp);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n-1;i++){
scanf("%d %d",&p,&q);
add(p,q);
add(q,p);
}
dfs1(r,0,1);
dfs2(r,r);
build(1,1,n);
for(int i=1;i<=m;i++){
ans=0;
scanf("%d",&op);
if(op == 1){
scanf("%d %d %lld",&x,&y,&z);
updrange(x,y,z);
}
else if(op == 2){
scanf("%d %d",&x,&y);
qrange(x,y);
printf("%lld\n",ans);
}
else if(op == 3){
scanf("%d %lld",&x,&z);
updson(x,z);
}
else{
scanf("%d",&x);
qson(x);
printf("%lld\n",ans);
}
}
return 0;
}