樹鏈剖分套樹狀數組(區間修改)板子
阿新 • • 發佈:2017-11-10
c代碼 msu style ont 重要 pri ott src inline
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≤10,M≤10
對於70%的數據: N≤103,M≤103
對於100%的數據: N≤105,M≤105
( 其實,純隨機生成的樹LCA+暴力是能過的,可是,你覺得可能是純隨機的麽233 )
樣例說明:
樹的結構如下:
各個操作如下:
故輸出應依次為2、21(重要的事情說三遍:記得取模)
賽前提高一下碼題能力.
吐槽一下,寫了1hour的樹剖【吐血】.
鏈接:https://www.luogu.org/problemnew/show/3384
AC代碼:
#include<cstdio> #include<iostream> #define FOR(i,s,t) for(register int i=s;i<=t;++i) typedef long long ll; const int maxn=100011; int n,m,r,p,type,x,y; ll z; int a[maxn]; #define VIS(now) for(register int e=las[now];e;e=nxt[e]) namespace TCP_And_BIT{ ll tr1[maxn],tr2[maxn],num[maxn]; inline int lowbit(int x){ return x&(-x); } inline void add(ll *r,int pos,ll x){ for(;pos<=n;r[pos]+=x,r[pos]%=p,pos+=lowbit(pos));return; } inline void _add(int l,int r,ll x){ x%=p; add(tr1,l,x);add(tr1,r+1,(-x+p)%p); add(tr2,l,1ll*(l-1)*x%p);add(tr2,r+1,1ll*(p-x)%p*r); } inline ll query(ll *r,int pos){ ll ans=0;for(;pos;ans+=r[pos],ans%=p,pos-=lowbit(pos));return ans; } inline ll _query(int l,int r){ ll sum1,sum2; sum1=1ll*(l-1)*query(tr1,l-1)-1ll*query(tr2,l-1); sum2=1ll*r*query(tr1,r)-1ll*query(tr2,r); return (sum2-sum1+2*p)%p; } int nxt[maxn<<1],las[maxn],to[maxn<<1]; int dep[maxn],sz[maxn],f[maxn],top[maxn],xu[maxn]; int tot,dfn; inline void insert(int x,int y){ nxt[++tot]=las[x];las[x]=tot;to[tot]=y;return; } inline void dfs1(int now){ sz[now]=1; VIS(now) if(!dep[to[e]]){ dep[to[e]]=dep[now]+1;f[to[e]]=now; dfs1(to[e]);sz[now]+=sz[to[e]]; } return; } inline void dfs2(int now,int chain){ xu[now]=++dfn; num[dfn]=a[now]; top[now]=chain; add(tr1,dfn,1ll*(num[dfn]-num[dfn-1]+p)%p); add(tr2,dfn,1ll*(dfn-1)%p*(num[dfn]-num[dfn-1]+p)%p); register int i=0; VIS(now)if(to[e]!=f[now]&&sz[i]<sz[to[e]])i=to[e]; if(!i)return; dfs2(i,chain); VIS(now)if(to[e]!=f[now]&&i!=to[e])dfs2(to[e],to[e]); return; } inline ll query_sum(int x,int y){ ll ans=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])x^=y^=x^=y; ans+=_query(xu[top[x]],xu[x]);ans=(ans+2*p)%p;x=f[top[x]]; } if(dep[x]<dep[y])x^=y^=x^=y; ans+=_query(xu[y],xu[x]);ans=(ans+2*p)%p; return ans; } inline void add_num(int x,int y,ll v){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])x^=y^=x^=y; _add(xu[top[x]],xu[x],v);x=f[top[x]]; } if(dep[x]<dep[y])x^=y^=x^=y; _add(xu[y],xu[x],v);return ; } }; using namespace TCP_And_BIT; int main(){ scanf("%d%d%d%d",&n,&m,&r,&p); FOR(i,1,n)scanf("%d",a+i),a[i]%=p; FOR(i,2,n){ scanf("%d%d",&x,&y); insert(x,y);insert(y,x); } dep[r]=1; dfs1(r);dfs2(r,r); while(m--){ scanf("%d",&type); if(type==1){ scanf("%d%d%lld",&x,&y,&z); z%=p; add_num(x,y,z); } if(type==2){ scanf("%d%d",&x,&y); printf("%lld\n",(query_sum(x,y)+p)%p); } if(type==3){ scanf("%d%lld",&x,&z); z%=p; _add(xu[x],xu[x]+sz[x]-1,z); } if(type==4){ scanf("%d",&x); printf("%lld\n",(_query(xu[x],xu[x]+sz[x]-1)+p)%p); } } return 0; }
樹鏈剖分套樹狀數組(區間修改)板子