[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 5
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
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
Sample Output
6
9
13
9
13
裸的树链剖分.....线段树维护两个dfs建树...mx[i]记录所到子树最大的id编号,方便区间加...因为dfs子树编号都是连续的
lazy维护区间加,单点加就是自己到自己的区间加.没什么.开始以为树链剖分好难
#include<cstdio> #include<cstring> #include<iostream> #include<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 ",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; }