luogu 3384 【模板】樹鏈剖分
P3384 【模板】樹鏈剖分
題目描述
如題,已知一棵包含N個結點的樹(連通且無環),每個節點上包含一個數值,需要支持以下操作:
操作1: 格式: 1 x y z 表示將樹從x到y結點最短路徑上所有節點的值都加上z
操作2: 格式: 2 x y 表示求樹從x到y結點最短路徑上所有節點的值之和
操作3: 格式: 3 x z 表示將以x為根節點的子樹內所有節點值都加上z
操作4: 格式: 4 x 表示求以x為根節點的子樹內所有節點值之和
輸入輸出格式
輸入格式:
第一行包含4個正整數N、M、R、P,分別表示樹的結點個數、操作個數、根節點序號和取模數(即所有的輸出結果均對此取模)。
接下來一行包含N個非負整數,分別依次表示各個節點上初始的數值。
接下來N-1行每行包含兩個整數x、y,表示點x和點y之間連有一條邊(保證無環且連通)
接下來M行每行包含若幹個正整數,每行表示一個操作,格式如下:
操作1: 1 x y z
操作2: 2 x y
操作3: 3 x z
操作4: 4 x
輸出格式:
輸出包含若幹行,分別依次表示每個操作2或操作4所得的結果(對P取模)
輸入輸出樣例
輸入樣例#1: 復制5 5 2 24 7 3 7 8 0 1 2 1 5 3 1 4 1 3 4 2 3 2 2 4 5 1 5 1 3 2 1 3輸出樣例#1: 復制
2 21
說明
時空限制:1s,128M
數據規模:
對於30%的數據: N \leq 10, M \leq 10N≤10,M≤10
對於70%的數據: N \leq {10}^3, M \leq {10}^3N≤103,M≤103
對於100%的數據: N \leq {10}^5, M \leq {10}^5N≤105,M≤105
( 其實,純隨機生成的樹LCA+暴力是能過的,可是,你覺得可能是純隨機的麽233 )
樣例說明:
樹的結構如下:
各個操作如下:
故輸出應依次為2、21(重要的事情說三遍:記得取模)
樹鏈剖分模板題。
說說我敲這個模板題時的感受吧。
代碼量是真的大!在線段樹的基礎上進行四種操作外加兩個dfs,要有點心理準備。
主要的思想就是把一棵樹按重兒子優先的dfs序在葉子結點後分開,從而分成很多條鏈。這樣就可以在線段樹上進行維護了。
有很多數組,不過都有很明確的應用,也不可取代。
qrange,updrange的思想就是在鏈頭較深的鏈上跳來跳去,還是很有意思的。
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <string> #include <cmath> #include <queue> #include <deque> #include <stack> #include <map> #include <set> typedef long long ll; const int mod=1000000007; const int inf=1000000000; const int maxn=100000; const int maxm=1000000; int n,m,r,p; int val[maxn+10]; int to[maxn*2+10]; int next[maxn*2+10]; int head[maxn+10],cnt; int depth[maxn+10];//節點深度 int siz[maxn+10];//節點子樹大小 int son[maxn+10];//有子節點的節點重兒子編號,沒有為0 int fa[maxn+10];//父親節點編號 void dfs1(int x,int f,int d) { fa[x]=f; depth[x]=d; for(int i=head[x];i!=-1;i=next[i]) { int l=to[i]; if(l!=f) { dfs1(l,x,d+1); siz[x]+=siz[l]; if(son[x]==0||siz[l]>siz[son[x]]) son[x]=l; } } siz[x]++; } int id[maxn+10],num;//原編號到dfs序的映射 int wt[maxn+10];//由dfs序得到節點權重 int top[maxn+10];//鏈頂端的編號 //dfs序只和id、wt數組有關,其余數組涉及的都是原編號,線段樹部分完全用的是dfs序 void dfs2(int x,int topf) { id[x]=num++; wt[id[x]]=val[x]; top[x]=topf; if(son[x]==0) return; dfs2(son[x],topf); for(int i=head[x];i!=-1;i=next[i]) { int l=to[i]; if(l!=fa[x]&&son[x]!=l) dfs2(l,l); } } void init() { cnt=0; num=1; memset(head,-1,sizeof(head)); memset(siz,0,sizeof(siz)); memset(son,0,sizeof(son)); } /*****以下是線段樹部分(註意Tag的寫法哦)*****/ struct ttree { int l,r; int tag; int sum; inline int len() { return r-l+1; } }; ttree tree[maxn*4+10]; void pushup(int x) { if(tree[x].l==tree[x].r) return; tree[x].sum=(tree[x*2].sum+tree[x*2+1].sum)%p; } void pushdown(int x) { if(tree[x].l==tree[x].r) return; int tag=tree[x].tag; tree[x].tag=0; tree[x*2].tag+=tag; tree[x*2+1].tag+=tag; tree[x*2].sum=(tree[x*2].sum+tag*tree[x*2].len())%p; tree[x*2+1].sum=(tree[x*2+1].sum+tag*tree[x*2+1].len())%p; } void build(int x,int l,int r) { tree[x].l=l; tree[x].r=r; tree[x].tag=0; if(tree[x].l==tree[x].r) tree[x].sum=wt[tree[x].l];//wt數組的 else { int mid=(l+r)/2; build(x*2,l,mid); build(x*2+1,mid+1,r); pushup(x); } } void modify(int x,int l,int r,int op) { if(l<=tree[x].l&&r>=tree[x].r) { tree[x].tag+=op; tree[x].sum=(tree[x].sum+op*tree[x].len())%p; } else { pushdown(x); int mid=(tree[x].l+tree[x].r)/2; if(l<=mid) modify(x*2,l,r,op); if(r>=mid+1) modify(x*2+1,l,r,op); pushup(x); } } int query(int x,int l,int r) { if(l<=tree[x].l&&r>=tree[x].r) return tree[x].sum; else { pushdown(x); int res=0; int mid=(tree[x].l+tree[x].r)/2; if(l<=mid) res=(res+query(x*2,l,r))%p; if(r>=mid+1) res=(res+query(x*2+1,l,r))%p; return res; } } /*****以上是線段樹部分*****/ int qrange(int x,int y) { int ans=0; while(top[x]!=top[y]) { if(depth[top[x]]<depth[top[y]]) std::swap(x,y); int res=query(1,id[top[x]],id[x]); ans=(ans+res)%p; x=fa[top[x]]; } if(depth[x]<depth[y]) std::swap(x,y); int res=query(1,id[y],id[x]); ans=(ans+res)%p; return ans; } void updrange(int x,int y,int z) { z%=p; while(top[x]!=top[y]) { if(depth[top[x]]<depth[top[y]]) std::swap(x,y); modify(1,id[top[x]],id[x],z); x=fa[top[x]]; } if(depth[x]<depth[y]) std::swap(x,y); modify(1,id[y],id[x],z); } int qson(int x) { return query(1,id[x],id[x]+siz[x]-1); } void updson(int x,int z) { modify(1,id[x],id[x]+siz[x]-1,z); } int main() { init(); scanf("%d%d%d%d",&n,&m,&r,&p); for(int i=1;i<=n;i++) scanf("%d",val+i); for(int i=1,a,b;i<n;i++) { scanf("%d%d",&a,&b); to[cnt]=b;next[cnt]=head[a];head[a]=cnt++; to[cnt]=a;next[cnt]=head[b];head[b]=cnt++; } dfs1(r,-1,1); dfs2(r,r); build(1,1,n); while(m--) { int op,x,y,z; scanf("%d",&op); if(op==1) { scanf("%d%d%d",&x,&y,&z); updrange(x,y,z); } if(op==2) { scanf("%d%d",&x,&y); printf("%d\n",qrange(x,y)); } if(op==3) { scanf("%d%d",&x,&z); updson(x,z); } if(op==4) { scanf("%d",&x); printf("%d\n",qson(x)); } } return 0; }View Code
luogu 3384 【模板】樹鏈剖分