[HAOI2015]樹上操作
阿新 • • 發佈:2017-09-07
一個 -s name sam pla etc 答案 pre scanf
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3
9
13
裸的樹鏈剖分.....線段樹維護兩個dfs建樹...mx[i]記錄所到子樹最大的id編號,方便區間加...因為dfs子樹編號都是連續的 lazy維護區間加,單點加就是自己到自己的區間加.沒什麽.開始以為樹鏈剖分好難
[HAOI2015]樹上操作
2017-09-07
Description
有一棵點數為 N 的樹,以點 1 為根,且樹點有邊權。然後有 M 個 操作,分為三種: 操作 1 :把某個節點 x 的點權增加 a 。 操作 2 :把某個節點 x 為根的子樹中所有點的點權都增加 a 。 操作 3 :詢問某個節點 x 到根的路徑中所有點的點權和。Input
第一行包含兩個整數 N, M 。表示點數和操作數。接下來一行 N 個整數,表示樹中節點的初始權值。接下來 N-1 行每行三個正整數 fr, to , 表示該樹中存在一條邊 (fr, to) 。再接下來 M 行,每行分別表示一次操作。其中 第一個數表示該操作的種類( 1-3 ) ,之後接這個操作的參數( x 或者 x a ) 。Output
對於每個詢問操作,輸出該詢問的答案。答案之間用換行隔開。
Sample Input
5 51 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3
Sample Output
69
13
裸的樹鏈剖分.....線段樹維護兩個dfs建樹...mx[i]記錄所到子樹最大的id編號,方便區間加...因為dfs子樹編號都是連續的 lazy維護區間加,單點加就是自己到自己的區間加.沒什麽.開始以為樹鏈剖分好難
#include<cstdio> #include<cstring> #include<iostream> #include樹上操作[s醬]<algorithm> #define ll long long const int maxn=100000+999; using namespace std; int read(){ int an=0,f=1;char ch=getchar(); while(!(‘0‘<=ch&&ch<=‘9‘)){if(ch==‘-‘)f=-f;ch=getchar();} while(‘0‘<=ch&&ch<=‘9‘){an=an*10+(ch-‘0‘);ch=getchar();} return f*an; } int v[maxn],cnt,fa[maxn],id,belong[maxn],mx[maxn],f[maxn],pos[maxn],deep[maxn];int n,m,size[maxn]; ll tag[maxn*4],sum[maxn*4]; bool vis[maxn]; struct sab{ int nex,to; }b[maxn<<1]; struct saber{ ll lazy,sum,l,r; }tr[maxn*4]; void add(int x,int y){ cnt++;b[cnt].nex=f[x]; f[x]=cnt;b[cnt].to=y;} void dfs(int x){ vis[x]=1;size[x]=1; for(int i=f[x];i;i=b[i].nex){ int v=b[i].to; if(!vis[v]){ fa[v]=x; deep[v]=deep[x]+1; dfs(v); size[x]+=size[v]; } } } void dfs2(int x,int chain){ id++;int k=0; mx[x]=pos[x]=id; belong[x]=chain; for(int i=f[x];i;i=b[i].nex){ int v=b[i].to; if(v!=fa[x]&&size[v]>size[k])k=v;} if(k){dfs2(k,chain);mx[x]=max(mx[x],mx[k]);} for(int i=f[x];i;i=b[i].nex){ int v=b[i].to; if(v!=fa[x]&&k!=v)dfs2(v,v); mx[x]=max(mx[x],mx[v]); } } void pushdown(int l,int r,int k) { if(l==r)return; int mid=(l+r)>>1;ll t=tag[k];tag[k]=0; tag[k<<1]+=t;tag[k<<1|1]+=t; sum[k<<1]+=t*(mid-l+1); sum[k<<1|1]+=t*(r-mid); } void add(int k,int l,int r,int x,int y,ll val) { if(tag[k])pushdown(l,r,k); if(l==x&&y==r){tag[k]+=val;sum[k]+=(r-l+1)*val;return;} int mid=(l+r)>>1; if(x<=mid)add(k<<1,l,mid,x,min(mid,y),val); if(y>=mid+1)add(k<<1|1,mid+1,r,max(mid+1,x),y,val); sum[k]=sum[k<<1]+sum[k<<1|1]; } ll ask(int k,int l,int r,int x,int y) { if(tag[k])pushdown(l,r,k); if(l==x&&y==r)return sum[k]; int mid=(l+r)>>1; ll ans=0; if(x<=mid) ans+=ask(k<<1,l,mid,x,min(mid,y)); if(y>=mid+1) ans+=ask(k<<1|1,mid+1,r,max(mid+1,x),y); return ans; } ll QUE(int x){ ll ans=0; while(belong[x]!=1){ ans+=ask(1,1,n,pos[belong[x]],pos[x]); x=fa[belong[x]]; } ans+=ask(1,1,n,1,pos[x]); return ans; } int main(){ n=read();m=read(); for(int i=1;i<=n;i++)v[i]=read(); for(int i=1;i<n;i++){ int x,y; x=read();y=read(); add(x,y);add(y,x); } dfs(1); dfs2(1,1); for(int i=1;i<=n;i++)add(1,1,n,pos[i],pos[i],v[i]); while(m){ m--; int x,y,z; x=read(); if(x==1){ y=read();z=read();//將y節點加z add(1,1,n,pos[y],pos[y],z);} else if(x==2){ y=read();z=read();//將y的子樹包括他本身+z add(1,1,n,pos[y],mx[y],z); } else{ y=read();//現在開始查詢從y到1的所有點權和.... printf("%lld\n",QUE(y)); } } return 0; }
by:s_a_b_e_r
樹鏈剖分……
因為是dfs所以每個子樹都是一段連續的區間
第二遍dfs的時候順便記錄一下從這個點到這個子樹結束的區間
然後就可以把子樹加轉換為區間加了
來人,上懶標簽……O(∩_∩)O~
(以及這題可以偷個懶,把單點加寫成長度為1的區間加)
#include<iostream> #include<cstdio> #define ll long long using namespace std; const int N=100009; int n,m,p[N],cnt,inx; int size[N],dep[N],fa[N],bl[N],pos[N],mx[N]; ll a[N]; struct node{ int l,r; ll sum,laz; }tree[N<<2]; struct edge{ int to,nex; }e[N<<1]; void adde(int u,int v) { ++cnt; e[cnt].to=v; e[cnt].nex=p[u]; p[u]=cnt; } void dfs1(int u) { size[u]=1; for(int i=p[u];i;i=e[i].nex) { int v=e[i].to; if(v==fa[u])continue; dep[v]=dep[u]+1; fa[v]=u; dfs1(v); size[u]+=size[v]; } } void dfs2(int u,int chain) { int k=0;++inx; pos[u]=mx[u]=inx; bl[u]=chain; for(int i=p[u];i;i=e[i].nex) { int v=e[i].to; if(dep[u]<dep[v]&&size[k]<size[v])k=v; } if(k){dfs2(k,chain);mx[u]=max(mx[u],mx[k]);} for(int i=p[u];i;i=e[i].nex) { int v=e[i].to; if(dep[u]<dep[v]&&k!=v) {dfs2(v,v);mx[u]=max(mx[u],mx[v]);} } } void build(int k,int l,int r) { tree[k].l=l,tree[k].r=r; if(l==r)return; int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } void add(int k,int p,ll w) { int l=tree[k].l,r=tree[k].r; if(l==r){tree[k].sum+=w;return;} int mid=(l+r)>>1; if(p<=mid)add(k<<1,p,w); else add(k<<1|1,p,w); tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum; } void addlaz(int k,ll w) { tree[k].sum+=(tree[k].r-tree[k].l+1)*w; tree[k].laz+=w; } void pushdown(int k) { addlaz(k<<1,tree[k].laz); addlaz(k<<1|1,tree[k].laz); tree[k].laz=0; } void addall(int k,int L,int R,ll w) { int l=tree[k].l,r=tree[k].r; if(r<L||l>R)return; if(L<=l&&r<=R) { tree[k].sum+=(r-l+1)*w; tree[k].laz+=w; return; } pushdown(k); addall(k<<1,L,R,w); addall(k<<1|1,L,R,w); tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum; } ll ask(int k,int L,int R) { int l=tree[k].l,r=tree[k].r; if(r<L||l>R)return 0; if(L<=l&&R>=r)return tree[k].sum; pushdown(k); return ask(k<<1,L,R)+ask(k<<1|1,L,R); } ll query(int x) { ll ans=0; while(bl[x]!=1) { ans+=ask(1,pos[bl[x]],pos[x]); x=fa[bl[x]]; } ans+=ask(1,1,pos[x]); return ans; } int main() { cin>>n>>m; for(int i=1;i<=n;++i)cin>>a[i]; for(int i=1;i<n;++i) { int x,y; scanf("%d%d",&x,&y); adde(x,y);adde(y,x); } dfs1(1);dfs2(1,1); build(1,1,n); for(int i=1;i<=n;++i)add(1,pos[i],a[i]); for(int i=1;i<=n;++i)cout<<pos[i]<<" "; cout<<endl; while(m--) { int t,x;ll w; cin>>t>>x; if(t==1){cin>>w;addall(1,pos[x],pos[x],w);} else if(t==2){cin>>w;addall(1,pos[x],mx[x],w);} else {cout<<query(x)<<endl;} } return 0; }[HAOI2015]樹上操作(w菌)
[HAOI2015]樹上操作