樹鏈剖分模板(洛谷P3384)
阿新 • • 發佈:2018-11-22
直接 void treenode mes pre getc problem bits dep
洛谷P3384
#include <bits/stdc++.h> #define DBG(x) cerr << #x << " = " << x << endl; const int maxn = 1e5+5; using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); while(ch < ‘0‘ || ch > ‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch >= ‘0‘ && ch <= ‘9‘){x=x*10+ch-‘0‘;ch=getchar();} return x*f; } int n,m,rot,mod; int w[maxn],wt[maxn];///剖分前後的點權,不要抄錯QAQ int head[maxn],tot; int cnt,dep[maxn],siz[maxn],fa[maxn],son[maxn],id[maxn],top[maxn];///son[i]表示i的重兒子,top[i]表示i所在鏈的頂點 int ans; struct treenode{ int l,r,val,add; }tree[maxn<<2]; struct edgenode{ int to,next; }edge[maxn<<1]; void addedge(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } void push_up(int x){ tree[x].val=tree[x<<1].val+tree[x<<1|1].val; tree[x].val%=mod; } void push_down(int x,int len){ if(tree[x].add){ tree[x<<1].add+=tree[x].add; tree[x<<1|1].add+=tree[x].add; tree[x<<1].val+=(len-(len>>1))*tree[x].add; tree[x<<1|1].val+=(len>>1)*tree[x].add; tree[x<<1].val%=mod; tree[x<<1|1].val%=mod; tree[x].add=0; } } void build(int i,int l,int r){ tree[i].l=l; tree[i].r=r; tree[i].add=0; if(l == r){ tree[i].val=wt[l]; tree[i].val%=mod; return; } int mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); push_up(i); } void update(int i,int l,int r,int L,int R,int c){ if(l >= L && r <= R){ tree[i].val+=(r-l+1)*c; tree[i].add+=c; return; } push_down(i,r-l+1); int mid=(l+r)>>1; if(L <= mid)update(i<<1,l,mid,L,R,c); if(R > mid)update(i<<1|1,mid+1,r,L,R,c); push_up(i); } void query(int i,int l,int r,int L,int R){ if(l >= L && r <= R){ ans+=tree[i].val; ans%=mod; return; } push_down(i,r-l+1); int mid=(l+r)>>1; if(L <= mid)query(i<<1,l,mid,L,R); if(R > mid)query(i<<1|1,mid+1,r,L,R); } void dfs1(int x,int pre,int deep){///確定各節點層次,子樹大小,父節點編號,重兒子編號 dep[x]=deep; fa[x]=pre; siz[x]=1; int maxson=-1; for(int i=head[x];i != -1;i=edge[i].next){ int v=edge[i].to; if(v != pre){ dfs1(v,x,deep+1); siz[x]+=siz[v]; if(siz[v] > maxson){maxson=siz[v];son[x]=v;} } } } void dfs2(int x,int topf){///確定各鏈的信息,包括鏈的頂點以及鏈上點的新編號 id[x]=++cnt; wt[id[x]]=w[x]; top[x]=topf; if(!son[x])return; dfs2(son[x],topf); for(int i=head[x];i != -1;i=edge[i].next){ int v=edge[i].to; if(v == fa[x] || v == son[x])continue; dfs2(v,v); } } void spilt(){ dfs1(rot,0,1); dfs2(rot,rot); build(1,1,n); } /*查詢兩點路徑上的點權和*/ int qRange(int x,int y){///讓深度深的點往上跳到頂點並更新對答案的貢獻 int res=0; ///之後更新到舊鏈頂上的鏈,重復過程直到兩點在同一條鏈上 while(top[x] != top[y]){ if(dep[top[x]] < dep[top[y]])swap(x,y); ans=0; query(1,1,n,id[top[x]],id[x]); res+=ans; res%=mod; x=fa[top[x]]; } if(dep[x] > dep[y])swap(x,y); ans=0; query(1,1,n,id[x],id[y]); res+=ans; return res%mod; } /*更新同查詢*/ void updRange(int x,int y,int c){ c%=mod; while(top[x] != top[y]){ if(dep[top[x]] < dep[top[y]])swap(x,y); update(1,1,n,id[top[x]],id[x],c); x=fa[top[x]]; } if(dep[x] > dep[y])swap(x,y); update(1,1,n,id[x],id[y],c); } /*查詢子樹點權和*/ int qSon(int x){///由於新編號連續,直接查詢樹根到樹根+樹的大小,這段區間對應整個子樹 ans=0; query(1,1,n,id[x],id[x]+siz[x]-1); return ans; } /*更新同查詢*/ void updSon(int x,int c){ update(1,1,n,id[x],id[x]+siz[x]-1,c); } int main(){ memset(head,-1,sizeof head); n=read(),m=read(),rot=read(),mod=read(); for(int i=1;i<=n;i++)w[i]=read(); for(int i=1;i<=n-1;i++){ int a=read(),b=read(); addedge(a,b); addedge(b,a); } spilt();///剖分 for(int i=1;i<=m;i++){ int op,x,y,z; op=read(); if(op == 1){ x=read(),y=read(),z=read(); updRange(x,y,z); } if(op == 2){ x=read(),y=read(); printf("%d\n",qRange(x,y)); } if(op == 3){ x=read(),y=read(); updSon(x,y); } if(op == 4){ x=read(); printf("%d\n",qSon(x)); } } return 0; }
樹鏈剖分模板(洛谷P3384)