題外話:ios
一道至今爲止作題時間最長的題:編程
begin at 8.30A.M函數
而後求助_yjk dalao後ui
最後一次搞取模:spa
awsl。指針
正解開始:code
題目連接。blog
樹鏈剖分,指的是將一棵樹經過兩次遍歷後將一棵樹分紅重鏈,輕邊的過程。get
咱們定義:string
重兒子:每一個點的子樹中,子樹大小(即節點數)最大的子節點
輕兒子:除重兒子外的其餘子節點
重邊:每一個節點與其重兒子間的邊
輕邊:每一個節點與其輕兒子間的邊
重鏈:重邊連成的鏈
輕鏈:輕邊連成的鏈(目前沒用到過,仍是太菜)
因而乎,咱們來舉個栗子:
其中,紅色邊爲重邊,天然,它們組成的鏈就是重鏈嘍。你能夠按照定義從根節點往下人腦dfs(???)一遍模擬過程,方便理解dfs代碼。
窩們發現:重鏈和輕邊交替出現(沒什麼用)。
而後,理解完上面的部分呢,咱們先安排一下兩個dfs,用來剖分整棵樹以及dfs過程當中順便搞一下其餘東西(面向題目編程)。。
第一個dfs:
做用:求出每個節點的深度dep,定義father指針fa[]指向本身的父親節點,求出子樹的大小size並順便找到重兒子son
代碼以下:
inline void dfs1(int now,int f,int deep) { dep[now]=deep;//now指的是當前節點 fa[now]=f;//指向父親節點 size[now]=1;//加上本身 int maxn=-1,maxson=0;//初始 for(int i=hea[now];i;i=edge[i].next)//鏈式存圖 { int v=edge[i].to; if(v==f)continue;//不要再找回去了。。 dfs1(v,now,deep+1);//先把本身子樹的信息肯定好 size[now]+=size[v];//這時候size[v]已經肯定了,咱們就往上統計大小 if(size[v]>maxn)maxson=v,maxn=size[v];//根據定義,求出子樹最大的兒子重兒子 } son[now]=maxson;//for完了就找最大的賦值 }
到這裏,咱們成功地把重兒子都給找了出來,下一步差連成鏈了。
第二遍dfs:
在第一遍dfs已經找到了重兒子的基礎上,咱們將每一條重鏈上的節點的編號弄成相鄰的,也就是一條重鏈上節點編號相鄰,這樣保證了一條重鏈能夠被劃分爲一個區間,方便之後的跳躍和區間查詢操做。
天然地,咱們須要在遍歷時先遍歷重兒子來保證區間順序相連。
代碼以下:
inline void dfs2(int now,int ttop)//top表明一個重鏈的頭,也就是dfs序最小的那個 { dfn[now]=++num2;//num1在鏈式前向星中用到了。233 a0[num2]=a[now];//新的順序,來使一條重鏈上節點編號相鄰 top[now]=ttop;//記指針指向一條重鏈中的鏈頭。 if(!son[now])return;//若是沒兒子,直接返回了 dfs2(son[now],ttop);//先搜重兒子 for(int i=hea[now];i;i=edge[i].next) { int v=edge[i].to; if(v==fa[now]||v==son[now])continue;//father和son已經搜過了 dfs2(v,v);//以當前節點爲重鏈頭部搜下去,而再也不是ttop } }
樹鏈剖分主體部分完結。。。(大霧)
由於最重要的部分不是剖分而是與其餘知識結合。。(霾)
最重要的部分(也是我一直不懂的部分):
關於詢問樹上兩點路徑權值之和以及修改權值之和的問題:
天然,咱們要把路徑檢索出來。。
衆所周知,對於一棵樹,任意兩個點間,有且僅有一條路徑,且這條路徑通過他們的LCA。這說明,在查詢和修改兩點路徑時,咱們能夠將樹鏈剖分求lca與線段樹區間修改有機結合?。
怎麼結合呢:
注意一下描述:
對於咱們要求的兩個點的lca,容易想到加速的方法爲:順着一條樹鏈往上跳,直接跳到樹鏈頭部,這樣一次可以跨越整條樹鏈。具體的方法:
1.找到深度更大的那個節點。
2.若是兩個節點所在重鏈的頭部不爲同一個節點的話,說明他們的lca比他們的樹鏈頭部的深度還要淺,那麼咱們就能夠將深度較深的節點跳到他的上一個重鏈的尾部(也就是x=fa[top[x]])來繼續查找,這樣咱們就能夠跳過一整條重鏈了。
(2的話呢,使勁循環
3.當再也不知足上面的條件,就說明他們已經在一條重鏈中了。那麼天然的,深度更小,在上面的節點爲他們的lca。
那麼,順着這個思路,咱們在跳太重鏈的時候順便query一下整個重鏈,一直query到lca。就完成了整條路徑上的查詢。
錯!
你會發現,還差那麼一段沒有被覆蓋上(綠色):
那麼,咱們只須要這樣一段指令:
再把x和y相差的那一段區間再查一下就能夠了。
路徑查詢部分代碼:
inline int querylj(int x,int y) { int ans=0; while(top[x]!=top[y])//尚未跳到同一個樹鏈上的話 { if(dep[top[x]]<dep[top[y]])swap(x,y);//把x做爲深度更深的節點方便處理 ans+=query(1,1,n,dfn[top[x]],dfn[x])%p;//一次查詢從鏈頭到尾部 ans%=p; x=fa[top[x]];//跳躍 } if(dep[x]>dep[y])swap(x,y); ans+=query(1,1,n,dfn[x],dfn[y])%p;//最後一段的查詢 return ans%p; }
至於區間路徑修改,一個原理。
就是把query函數換成update區間修改,而後函數沒有返回值罷了。
代碼:
inline void queryupdlj(int x,int y,int k)//註釋就不加了 { int ans=0; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]])swap(x,y); update(1,1,n,dfn[top[x]],dfn[x],k); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); update(1,1,n,dfn[x],dfn[y],k); }
至於子樹修改,更簡單了:
天然而然的,由於dfs續的緣故,咱們不難想到一個子樹內全部的節點的dfs序都是一個連續區間,區間開頭是子樹根節點的dfn,區間長度爲子樹大小size,那麼對應的區間就爲(設根節點爲x的話)x到x+size[x]-1。
因此這就是個簡單的區間修改,一步到位。
inline int queryson(int x)//查詢 {return query(1,1,n,dfn[x],dfn[x]+size[x]-1);} inline void queryupdson(int x,int k)//修改{update(1,1,n,dfn[x],dfn[x]+size[x]-1,k);}
此題代碼:
#include<cstdio> #include<cstring> #include<queue> #include<cmath> #include<iostream> #define N 100003 using namespace std; void swap(int &x,int &y){int temp=x;x=y,y=temp;} int read() { int ans=0; char ch=getchar(),last=' '; while(ch<'0'||ch>'9')last=ch,ch=getchar(); while(ch>='0'&&ch<='9')ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar(); return last=='-'?-ans:ans; } struct edg{ int next,to; }edge[N*2]; int n,fr,to,m,r,p,a0[N],a[N],a1[N*6],son[N],top[N],size[N],fa[N],dep[N],dfn[N],num1,num2,hea[N],lazy[N*6]; inline void add(int from,int to){num1++;edge[num1]=(edg){hea[from],to};hea[from]=num1;} inline void pushdown(int rt,int lenn){ lazy[rt<<1]+=lazy[rt]%p; lazy[rt<<1|1]+=lazy[rt]%p; a1[rt<<1]+=lazy[rt]*(lenn-(lenn>>1))%p; a1[rt<<1|1]+=lazy[rt]*(lenn>>1)%p; a1[rt<<1]%=p; a1[rt<<1|1]%=p; lazy[rt]=0; } inline void build(int l,int r,int now) { if(l==r) { a1[now]=a0[l];a1[now]%=p;return; } int mid=(l+r)>>1; build(l,mid,now<<1); build(mid+1,r,now<<1|1); a1[now]=(a1[now<<1]+a1[now<<1|1])%p; } void update(int now,int l,int r,int L,int R,int k) { if(l>=L&&r<=R){lazy[now]+=k%p;a1[now]+=k*(r-l+1)%p,a1[now]%=p;return;} if(lazy[now])pushdown(now,r-l+1); int mid=(l+r)>>1; if(L<=mid)update(now<<1,l,mid,L,R,k); if(R>mid)update(now<<1|1,mid+1,r,L,R,k); a1[now]=(a1[now<<1]+a1[now<<1|1])%p; // printf("1次upd\n"); } inline int query(int now,int l,int r,int L,int R) { int ans=0; if(l>=L&&r<=R){ans+=a1[now]%p,ans%=p;return ans;} if(lazy[now])pushdown(now,r-l+1); int mid=(l+r)>>1; if(L<=mid)ans+=query(now<<1,l,mid,L,R),ans%=p; if(R>mid)ans+=query(now<<1|1,mid+1,r,L,R),ans%=p; return ans; // printf("1次query\n"); } inline void dfs1(int now,int f,int deep) { dep[now]=deep; fa[now]=f; size[now]=1; int maxn=-1,maxson=0; for(int i=hea[now];i;i=edge[i].next) { int v=edge[i].to; if(v==f)continue; dfs1(v,now,deep+1); size[now]+=size[v]; if(size[v]>maxn)maxson=v,maxn=size[v]; } son[now]=maxson; } inline void dfs2(int now,int ttop) { dfn[now]=++num2; a0[num2]=a[now]; top[now]=ttop; if(!son[now])return; dfs2(son[now],ttop); for(int i=hea[now];i;i=edge[i].next) { int v=edge[i].to; if(v==fa[now]||v==son[now])continue; dfs2(v,v); } } inline int querylj(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,1,n,dfn[top[x]],dfn[x])%p; ans%=p; x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); ans+=query(1,1,n,dfn[x],dfn[y])%p; return ans%p; } inline void queryupdlj(int x,int y,int k) { int ans=0; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]])swap(x,y); update(1,1,n,dfn[top[x]],dfn[x],k); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); update(1,1,n,dfn[x],dfn[y],k); } inline int queryson(int x){return query(1,1,n,dfn[x],dfn[x]+size[x]-1);} inline void queryupdson(int x,int k){update(1,1,n,dfn[x],dfn[x]+size[x]-1,k);} int main() { n=read(),m=read(),r=read(),p=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n-1;i++) { fr=read(),to=read(); add(fr,to);add(to,fr); } dfs1(r,0,1); dfs2(r,r); build(1,n,1); for(int i=1;i<=m;i++) { int k,x1,y1,z1; k=read(); if(k==1){ x1=read(),y1=read(),z1=read(); queryupdlj(x1,y1,z1); } else if(k==2){ x1=read();y1=read(); printf("%d\n",querylj(x1,y1)%p); } else if(k==3){ x1=read();y1=read(); queryupdson(x1,y1); } else{ x1=read(); printf("%d\n",queryson(x1)%p); } } }
心力交猝。。
完結。
但願對各位有所幫助,有什麼不對的地方評論指出。