题目见洛谷。
下面就是单纯贴一下根据上面博客思路写的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;
}