前情提要,我主要看的是這位大佬的講解,用的是谷的程式碼,所以會有點奇怪
大概就是這麼個意思
dfs1用來處理樹的dfs序,處理出重鏈大小和對應的重兒子
void dfs1(int now){
son[now]=-1;
siz[now]=1;
for(int i=head[now];i;i=edge[i].from){
int to=edge[i].to;
if(dep[to]) continue;//有長度說明已經算過了,正在往回走(因為建的雙向邊)
dep[to]=dep[now]+1;
fa[to]=now;
dfs1(to);
siz[now]+=siz[to];//加上所有子樹大小得到本棵樹的大小
if(son[now]==-1||siz[to]>siz[son[now]]) son[now]=to;//遞迴回來時已經求出了各個子樹的長度,要進行比較,找出重兒子
}
}
dfs2用來處理重鏈的頭top,以及將樹重新編號dfn,使其符合一條重鏈上編號連續的狀態,這樣我們就可以在樹上實現類似線段樹的區間修改操作
void dfs2(int now,int tp){
top[now]=tp;//tp為重鏈的頭(不屬於重兒子)
num++;
b[num]=a[now];
// pre[num]=now; 這個沒必要,可以被上一行的替代
dfn[now]=num;//先跑重鏈,確保其連續
if(son[now]==-1) return;
dfs2(son[now],tp);
for(int i=head[now];i;i=edge[i].from){
int to=edge[i].to;
if(to!=son[now]&&to!=fa[now]) dfs2(to,to);//重鏈跑完後跑輕鏈
}
}
看程式碼可知,重鏈上的編號一定是連續的(因為是沿著重兒子跑的),遞迴完後跑輕鏈,找出輕鏈中剩餘的重鏈(如圖中的點3)
然後是路徑求和treesum
帶圖就好理解多了,假設我要求3到6路徑上的和,那麼我先將3所處的重鏈和6所處的重鏈的和求出來,利用dfs2的連續標記和線段樹可以快速求和,然後6跳到1上,3跳到2的父親1上,然後3和6就在同一重鏈上,這樣我們直接利用兩點所處重鏈的連續標記再次利用線段樹求和
int treesum(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans+=query(1,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans+=query(1,dfn[x],dfn[y]);
return ans;
}
然後沒了,實際上仔細分析還是能分析出來的
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define lson id<<1
#define rson id<<1|1
const int N=1e5+10;
int n,m,r,mod;
int a[N],b[N];
struct node{
int from;
int to;
}edge[N<<2];
int head[N],num,cnt;
int fa[N],dep[N],top[N],son[N],dfn[N],idx[N],siz[N];
struct node1{
int l,r,cnt,lazy,sum;
}tr[N<<2];
void pushup(int id){
if(!tr[id].lazy) return;
tr[lson].lazy=(tr[lson].lazy+tr[id].lazy)%mod;
tr[rson].lazy=(tr[rson].lazy+tr[id].lazy)%mod;
tr[lson].sum=(tr[lson].sum+tr[id].lazy*tr[lson].cnt)%mod;
tr[rson].sum=(tr[rson].sum+tr[id].lazy*tr[rson].cnt)%mod;
tr[id].lazy=0;
}
void build(int id,int l,int r){
tr[id].l=l;
tr[id].r=r;
tr[id].cnt=r-l+1;
if(l==r){
tr[id].sum=a[l]%mod;
return;
}
int mid=(l+r)/2;
build(lson,l,mid);
build(rson,mid+1,r);
tr[id].sum=(tr[lson].sum+tr[rson].sum)%mod;
}
void update(int id,int l,int r,int ad){
if(l>tr[id].r||r<tr[id].l) return;
if(l<=tr[id].l&&tr[id].r<=r) {
tr[id].sum=(ad*tr[id].cnt+tr[id].sum)%mod;
tr[id].lazy=(tr[id].lazy+ad)%mod;
return;
}
pushup(id);
// int mid=(tr[id].l+tr[id].r)/2;
update(lson,l,r,ad);
update(rson,l,r,ad);
tr[id].sum=(tr[lson].sum+tr[rson].sum)%mod;
}
int getsum(int id,int l,int r){
if(l>tr[id].r||r<tr[id].l) return 0;
if(l<=tr[id].l&&tr[id].r<=r) {
return tr[id].sum%mod;
}
pushup(id);
// int mid=(tr[id].l+tr[id].r)/2;
return getsum(lson,l,r)+getsum(rson,l,r);
}
void add(int from,int to){
cnt++;
edge[cnt].from=head[from];
edge[cnt].to=to;
head[from]=cnt;
}
void dfs1(int x){
son[x]=-1;
siz[x]=1;
for(int i=head[x];i;i=edge[i].from){
int to=edge[i].to;
if(dep[to]) continue;
dep[to]=dep[x]+1;
fa[to]=x;
dfs1(to);
siz[x]+=siz[to];
if(son[x]==-1||siz[to]>siz[son[x]]) son[x]=to;
}
}
void dfs2(int x,int tp){
top[x]=tp;
num++;
idx[num]=x;
dfn[x]=num;
a[num]=b[x];
if(son[x]==-1) return;
dfs2(son[x],tp);
for(int i=head[x];i;i=edge[i].from){
int to=edge[i].to;
if(to!=son[x]&&to!=fa[x]) dfs2(to,to);
}
}
void addtree(int x,int y,int ad){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
update(1,dfn[top[x]],dfn[x],ad);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
update(1,dfn[x],dfn[y],ad);
}
int treesum(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=(ans+getsum(1,dfn[top[x]],dfn[x]))%mod;
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans=(ans+getsum(1,dfn[x],dfn[y]))%mod;
return ans;
}
int main(){
int c,from,to,w;
cin>>n>>m>>r>>mod;
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<n;i++){
cin>>from>>to;
add(from,to);
add(to,from);
}
dep[r]=1;
dfs1(r);
dfs2(r,r);
build(1,1,num);
for(int i=1;i<=m;i++){
cin>>c;
if(c==1){
cin>>from>>to>>w;
addtree(from,to,w);
}
else if(c==2){
cin>>from>>to;
cout<<treesum(from,to)%mod<<endl;
}
else if(c==3){
cin>>from>>w;
update(1,dfn[from],dfn[from]+siz[from]-1,w);
}
else{
cin>>from;
cout<<getsum(1,dfn[from],dfn[from]+siz[from]-1)%mod<<endl;
}
}
}
注意一點,在正常的update下要用對應的dfn值
if(str=="CHANGE"){
cin>>root>>w;
update(1,dfn[root],w);
}//對的
if(str=="CHANGE"){
cin>>root>>w;
update(1,root,w);
}//錯的
還有一點,由於dfs一條路走到黑的特性,dfs2中樹的編號一定均為dfn[root]到dfn[root]+siz[root]-1(減一是因為左邊界包含了dfn[root],樹的總大小為siz[root]),因此直接update這些編號即可