1. 程式人生 > >P3384 【模板】樹鏈剖分

P3384 【模板】樹鏈剖分

描述 swap pro dfs pac 取模 pri oid 連通

chuansongmen

題目描述

如題,已知一棵包含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 10N10,M10

對於70%的數據: N \leq {10}^3, M \leq {10}^3N10?3??,M10?3??

對於100%的數據: N \leq {10}^5, M \leq {10}^5N10?5??,M10?5??

其實,純隨機生成的樹LCA+暴力是能過的,可是,你覺得可能是純隨機的麽233

樣例說明:

樹的結構如下:

技術分享

各個操作如下:

技術分享

故輸出應依次為2、21(重要的事情說三遍:記得取模)

code

#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=500005;
typedef 
long long LL; int n,m,u,v,t,r,sumedge,temp; LL mod; int head[maxn],son[maxn],dad[maxn],top[maxn],heavy_son[maxn]; int id[maxn],real[maxn],depth[maxn],a[maxn]; struct Edge{ int x,y,nxt; Edge(int x=0,int y=0,int nxt=0): x(x),y(y),nxt(nxt){} }edge[maxn]; struct segment_tree{ LL l,r,sum,tag; }tr[maxn]; void add(int x,int y){ edge[++sumedge]=Edge(x,y,head[x]); head[x]=sumedge; } void dfs1(int k,int fa){ dad[k]=fa;depth[k]=depth[fa]+1; son[k]=1; for(int i=head[k];i;i=edge[i].nxt){ int v=edge[i].y; if(v!=fa){ dfs1(v,k); son[k]+=son[v]; if(!heavy_son[k]||son[heavy_son[k]]<son[v]) heavy_son[k]=v; } } } void dfs2(int k,int fir){ top[k]=fir;id[k]=++temp; real[temp]=k; if(!heavy_son[k])return; dfs2(heavy_son[k],fir); for(int i=head[k];i;i=edge[i].nxt){ int v=edge[i].y; if(v!=dad[k]&&v!=heavy_son[k]) dfs2(v,v); } } void pushup(int rt){ tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum; } void pushdown(int rt){ tr[rt<<1].tag+=tr[rt].tag; tr[rt<<1].sum+=tr[rt].tag*(tr[rt<<1].r-tr[rt<<1].l+1); tr[rt<<1|1].tag+=tr[rt].tag; tr[rt<<1|1].sum+=tr[rt].tag*(tr[rt<<1|1].r-tr[rt<<1|1].l+1); tr[rt].tag=0; return; } void build(int rt,int l,int r){ tr[rt].l=l;tr[rt].r=r; if(l==r){ tr[rt].sum=a[real[l]]; return; } int mid=(l+r)>>1; build(rt<<1,l,mid);build(rt<<1|1,mid+1,r); pushup(rt); } void update(int now,int ll,int rr,int x){ if(ll<=tr[now].l&&tr[now].r<=rr){ tr[now].sum+=(tr[now].r-tr[now].l+1)*x; tr[now].tag+=x; return; } pushdown(now); int mid=(tr[now].l+tr[now].r)>>1; if(rr<=mid)update(now<<1,ll,rr,x); else if(ll>mid)update(now<<1|1,ll,rr,x); else { update(now<<1,ll,mid,x); update(now<<1|1,mid+1,rr,x); } pushup(now);//*** } LL query_sum(int now,int ll,int rr){ if(ll<=tr[now].l&&tr[now].r<=rr){ return tr[now].sum; } pushdown(now); int mid=(tr[now].l+tr[now].r)>>1; if(rr<=mid)return query_sum(now<<1,ll,rr); else if(ll>mid)return query_sum(now<<1|1,ll,rr); else{ return query_sum(now<<1,ll,mid)+query_sum(now<<1|1,mid+1,rr); } } void change(int u,int v,LL x){ int tu=top[u],tv=top[v]; while(tu!=tv){ if(depth[tu]<depth[tv]){ swap(tu,tv);swap(u,v); } update(1,id[tu],id[u],x); u=dad[tu]; tu=top[u]; } if(depth[u]>depth[v])swap(u,v); update(1,id[u],id[v],x); } int find_sum(int u,int v){ LL sum=0; int tu=top[u],tv=top[v]; while(tu!=tv){ if(depth[tu]<depth[tv]){ swap(tu,tv); swap(u,v); } sum+=query_sum(1,id[tu],id[u]); sum%=mod; u=dad[tu]; tu=top[u]; } if(depth[u]>depth[v])swap(u,v); sum+=query_sum(1,id[u],id[v]); return sum%mod; } void root_add(int now,LL x){ int begin=id[now]; int ed=id[now]+son[now]-1; update(1,begin,ed,x); } LL root_sum(int now){ int begin=id[now]; int ed=id[now]+son[now]-1; return query_sum(1,begin,ed)%mod; } int main(){ scanf("%d%d%d%lld",&n,&m,&r,&mod); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); add(u,v);add(v,u); } dfs1(r,0);dfs2(r,r); build(1,1,temp); for(int i=0;i<m;i++){ int t,x,y,z; scanf("%d",&t); if(t==1){ scanf("%d%d%d",&x,&y,&z); change(x,y,z); }else if(t==2){ scanf("%d%d",&x,&y); printf("%lld\n",find_sum(x,y)); }else if(t==3){ scanf("%d%d",&x,&y); root_add(x,y); }else if(t==4){ scanf("%d",&x); printf("%lld\n",root_sum(x)); } } return 0; }

P3384 【模板】樹鏈剖分