樹鏈剖分的基本思想是把一棵樹剖分紅若干條鏈,再利用線段樹等數據結構維護相關數據,能夠很是暴力優雅地解決不少問題。數據結構
樹鏈剖分中的幾個基本概念:
重兒子:對於當前節點的全部兒子中,子樹大小最大的一個兒子就是重兒子(子樹大小相同的則隨意取一個)
輕兒子:不是重兒子就是輕兒子
重邊:鏈接父節點和重兒子的邊
輕邊:鏈接父節點和輕兒子的邊
重鏈:相鄰重邊相連造成的鏈
值得注意的還有如下幾點:
葉子節點沒有重兒子也沒有輕兒子;
對於每一條重鏈,其起點必然是輕兒子;
單獨一個輕葉子節點也是一條重鏈;
結合上面三條能夠得出樹剖的一個性質:重鏈必然能夠囊括全部的節點。
(圖片來源百度圖片,侵刪)
紅點標記的是輕兒子,粗線就是重鏈。結合圖片理解概念。函數
樹鏈剖分須要怎麼作呢?
一、用DFS給每個節點標記深度,父節點和重兒子。
二、用DFS按照DFS遍歷的順序給每個節點標記新的編號。關鍵點:先處理重兒子再處理輕兒子
解釋:先處理重兒子可讓重鏈上的每個點的編號連續。能夠觀察上圖,線上的數字就是DFS的順序。使編號連續後,咱們就可使用線段樹來維護數據了。
作完以上兩步就算是完成了樹鏈剖分了,接下來要作的就是利用其它數據結構來進行維護了。ui
void add(ll sta,ll to) { edge[++cnt].to=to; edge[cnt].next=head[sta]; head[sta]=cnt; }//鏈式前向星存樹 void dfs1(ll now,ll fa,ll deep) { f[now]=fa;//記錄父節點 d[now]=deep;//記錄深度(深度在區間求和時會用到) size[now]=1;//記錄子樹大小 for (ll i=head[now];i!=0;i=edge[i].next) { if (edge[i].to==fa) continue; dfs1(edge[i].to,now,deep+1); size[now]+=size[edge[i].to]; if (size[edge[i].to]>size[wson[now]]) wson[now]=edge[i].to; //取重兒子 } } void dfs2(ll now,ll t) { top[now]=t;//記錄節點所在重鏈的起點 id[now]=++cnt;//按照順序編號 rk[cnt]=now;//記錄第cnt個點表示的是now節點,建樹時會用到 if (wson[now]) dfs2(wson[now],t);//優先處理重兒子 for (ll i=head[now];i!=0;i=edge[i].next) { if (edge[i].to==wson[now]) continue; if (edge[i].to==f[now]) continue; dfs2(edge[i].to,edge[i].to);//一條重鏈的開頭必然是輕兒子,鏈頭即爲它自己 } }
樹上兩點的最短路徑修改操做:spa
void treeupd(ll x,ll y,ll num) { while (top[x]!=top[y]) { if (d[top[x]]>d[top[y]]) { segupd(1,1,n,id[top[x]],id[x],num); //segupd爲線段樹的更新函數 x=f[top[x]]; } else { segupd(1,1,n,id[top[y]],id[y],num); //segupd爲線段樹的更新函數 y=f[top[y]]; } } //這一個循環的目的是,只要這兩個節點不在一條重鏈上, //就讓比較深的那一個往上跳到另外一條鏈直到二者在同一條鏈上 //又由於節點編號是連續的,因此能夠很方便地給整條鏈加上修改操做 if (id[x]<=id[y]) segupd(1,1,n,id[x],id[y],num); else segupd(1,1,n,id[y],id[x],num); //在最後二者位於同一條鏈上後,仍然要對他們兩個之間的節點進行修改。 }
求和操做再也不贅述,與上面的更新操做相似。code
完整代碼blog
#include<cstdio> #include<algorithm> #define lson root<<1 #define rson root<<1|1 #define ll long long #define mid ((l+r)>>1) using namespace std; struct data { ll to,next; }edge[200005]; ll cnt,head[200005],f[100005],d[100005],size[100005],wson[100005],top[100005],id[100005]; ll rk[100005],tree[800005],n,m,a[100005],p,tag[800005],r,x,y,z,flag; void add(ll sta,ll to) { edge[++cnt].to=to; edge[cnt].next=head[sta]; head[sta]=cnt; } void dfs1(ll now,ll fa,ll deep) { f[now]=fa; d[now]=deep; size[now]=1; for (ll i=head[now];i!=0;i=edge[i].next) { if (edge[i].to==fa) continue; dfs1(edge[i].to,now,deep+1); size[now]+=size[edge[i].to]; if (size[edge[i].to]>size[wson[now]]) wson[now]=edge[i].to; } } void dfs2(ll now,ll t) { top[now]=t; id[now]=++cnt; rk[cnt]=now; if (wson[now]) dfs2(wson[now],t); for (ll i=head[now];i!=0;i=edge[i].next) { if (edge[i].to==wson[now]) continue; if (edge[i].to==f[now]) continue; dfs2(edge[i].to,edge[i].to); } } void build(ll root,ll l,ll r) { if (l==r) { tree[root]=a[rk[l]]%p; return ; } build(lson,l,mid); build(rson,mid+1,r); tree[root]=(tree[lson]+tree[rson])%p; } void push_down(ll root,ll l,ll r) { if (tag[root]==0) return ; tag[lson]+=tag[root]; tag[rson]+=tag[root]; tree[lson]+=tag[root]*(mid-l+1); tree[rson]+=tag[root]*(r-mid); tag[lson]%=p; tag[rson]%=p; tree[lson]%=p; tree[rson]%=p; tag[root]=0; } void segupd(ll root,ll l,ll r,ll al,ll ar,ll num) { if (ar<l||r<al) return ; if (al<=l&&r<=ar) { tree[root]+=num*(r-l+1); tag[root]+=num; tree[root]%=p; tag[root]%=p; return ; } push_down(root,l,r); segupd(lson,l,mid,al,ar,num); segupd(rson,mid+1,r,al,ar,num); tree[root]=(tree[lson]+tree[rson])%p; } ll query(ll root,ll l,ll r,ll al,ll ar) { if (ar<l||r<al) return 0; if (al<=l&&r<=ar) return tree[root]%p; push_down(root,l,r); return (query(lson,l,mid,al,ar)+query(rson,mid+1,r,al,ar))%p; } ll getsum(ll x,ll y) { ll sum=0; while (top[x]!=top[y]) { if (d[top[x]]>d[top[y]]) { sum=(sum+query(1,1,n,id[top[x]],id[x]))%p; x=f[top[x]]; } else { sum=(sum+query(1,1,n,id[top[y]],id[y]))%p; y=f[top[y]]; } } if (id[x]<=id[y]) sum=(sum+query(1,1,n,id[x],id[y]))%p; else sum=(sum+query(1,1,n,id[y],id[x]))%p; return sum; } void treeupd(ll x,ll y,ll num) { while (top[x]!=top[y]) { if (d[top[x]]>d[top[y]]) { segupd(1,1,n,id[top[x]],id[x],num); x=f[top[x]]; } else { segupd(1,1,n,id[top[y]],id[y],num); y=f[top[y]]; } } if (id[x]<=id[y]) segupd(1,1,n,id[x],id[y],num); else segupd(1,1,n,id[y],id[x],num); } int main() { scanf("%lld%lld%lld%lld",&n,&m,&r,&p); for (ll i=1;i<=n;i++) scanf("%lld",&a[i]); for (ll i=1;i<n;i++) { scanf("%lld%lld",&x,&y); add(x,y); add(y,x); } cnt=0; dfs1(r,0,0); dfs2(r,r); build(1,1,n); for (ll i=1;i<=m;i++) { scanf("%lld",&flag); if (flag==1) { scanf("%lld%lld%lld",&x,&y,&z); treeupd(x,y,z); } if (flag==2) { scanf("%lld%lld",&x,&y); printf("%lld\n",getsum(x,y)); } if (flag==3) { scanf("%lld%lld",&x,&z); segupd(1,1,n,id[x],id[x]+size[x]-1,z); //這裏能夠結合圖片理解一下爲何。 } if (flag==4) { scanf("%lld",&x); printf("%lld\n",query(1,1,n,id[x],id[x]+size[x]-1)); } } return 0; }