[HAOI2015]树上操作
题目大意:
给你一棵n个点的树,以1为根,每个点都有一个点权,要求进行如下操作:
1.将x这个点的点权加上a;
2.将以x这个点为根的子树中每个点的点权加上a;
3.查询从x到根的路径的点权和。
思路:
树链剖分。
对于第2种操作,我们不难发现一个子树中结点在线段树中的编号一定是连续的。
于是修改子树的操作就是线段树上修改id[x]~id[x]+size[x]-1的操作。
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 typedef long long int64; 5 inline int getint() { 6 register char ch; 7 register bool neg=false; 8 while(!isdigit(ch=getchar())) if(ch=='-') neg=true; 9 register int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return neg?-x:x; 12 } 13 const int N=100001; 14 std::vector<int> e[N]; 15 inline void add_edge(const int &u,const int &v) { 16 e[u].push_back(v); 17 e[v].push_back(u); 18 } 19 int n,size[N],par[N],dep[N],id[N],son[N],top[N],w[N],node[N]; 20 void dfs1(const int &x,const int &par) { 21 ::par[x]=par; 22 dep[x]=dep[par]+1; 23 size[x]=1; 24 for(unsigned i=0;i<e[x].size();i++) { 25 const int &y=e[x][i]; 26 if(y==par) continue; 27 dfs1(y,x); 28 size[x]+=size[y]; 29 if(size[y]>size[son[x]]) son[x]=y; 30 } 31 } 32 void dfs2(const int &x) { 33 id[x]=++id[0]; 34 node[id[x]]=x; 35 if(x==son[par[x]]) { 36 top[x]=top[par[x]]; 37 } else { 38 top[x]=x; 39 } 40 if(son[x]) dfs2(son[x]); 41 for(unsigned i=0;i<e[x].size();i++) { 42 const int &y=e[x][i]; 43 if(y==par[x]||y==son[x]) continue; 44 dfs2(y); 45 } 46 } 47 class SegmentTree { 48 #define _left <<1 49 #define _right <<1|1 50 private: 51 int64 val[N<<2],tag[N<<2]; 52 void push_up(const int &p) { 53 val[p]=val[p _left]+val[p _right]; 54 } 55 int size(const int &b,const int &e) const { 56 return e-b+1; 57 } 58 void push_down(const int &p,const int &b,const int &e) { 59 const int mid=(b+e)>>1; 60 val[p _left]+=tag[p]*size(b,mid); 61 val[p _right]+=tag[p]*size(mid+1,e); 62 tag[p _left]+=tag[p]; 63 tag[p _right]+=tag[p]; 64 tag[p]=0; 65 } 66 public: 67 void build(const int &p,const int &b,const int &e) { 68 if(b==e) { 69 val[p]=w[node[b]]; 70 return; 71 } 72 const int mid=(b+e)>>1; 73 build(p _left,b,mid); 74 build(p _right,mid+1,e); 75 push_up(p); 76 } 77 void modify(const int &p,const int &b,const int &e,const int &l,const int &r,const int &x) { 78 if(b==l&&e==r) { 79 val[p]+=(int64)x*size(b,e); 80 tag[p]+=x; 81 return; 82 } 83 const int mid=(b+e)>>1; 84 push_down(p,b,e); 85 if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r),x); 86 if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r,x); 87 push_up(p); 88 } 89 int64 query(const int &p,const int &b,const int &e,const int &l,const int &r) { 90 if(b==l&&e==r) { 91 return val[p]; 92 } 93 const int mid=(b+e)>>1; 94 push_down(p,b,e); 95 int64 ret=0; 96 if(l<=mid) ret+=query(p _left,b,mid,l,std::min(mid,r)); 97 if(r>mid) ret+=query(p _right,mid+1,e,std::max(mid+1,l),r); 98 return ret; 99 } 100 #undef _left 101 #undef _right 102 }; 103 SegmentTree t; 104 inline int64 query(int x) { 105 int64 ret=0; 106 while(x) { 107 ret+=t.query(1,1,n,id[top[x]],id[x]); 108 x=par[top[x]]; 109 } 110 return ret; 111 } 112 int main() { 113 n=getint(); 114 const int m=getint(); 115 for(register int i=1;i<=n;i++) { 116 w[i]=getint(); 117 } 118 for(register int i=1;i<n;i++) { 119 add_edge(getint(),getint()); 120 } 121 dfs1(1,0); 122 dfs2(1); 123 t.build(1,1,n); 124 for(register int i=0;i<m;i++) { 125 const int op=getint(),x=getint(); 126 if(op==1) { 127 t.modify(1,1,n,id[x],id[x],getint()); 128 } 129 if(op==2) { 130 t.modify(1,1,n,id[x],id[x]+size[x]-1,getint()); 131 } 132 if(op==3) { 133 printf("%lld\n",query(x)); 134 } 135 } 136 return 0; 137 }
题目求的是点到根路径上的权值和,那么修改单点对整棵子树所有结点的询问都有贡献。
给整棵子树增加权值显然也会对整棵子树的询问产生贡献。
若v是u的子树中的一个点,对结点u为根的子树的每一个结点权值增加a,相当于给针对v的询问答案增加了(dis(u,v)+1)a。
考虑将树“压扁”,求出树的DFS序。 对于结点u的子树,DFS序范围是dfn[u]~dfn[u]+size[u]-1。
用线段树维护两个值x和y,分别代表需要乘dis的权值和不需要乘dis的权值,询问时返回dep[u]*x[u]+y[u]即可。
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 typedef long long int64; 5 inline int getint() { 6 register char ch; 7 register bool neg=false; 8 while(!isdigit(ch=getchar())) if(ch=='-') neg=true; 9 register int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return neg?-x:x; 12 } 13 const int N=100001; 14 int w[N],dfn[N],size[N],dep[N]; 15 std::vector<int> e[N]; 16 inline void add_edge(const int &u,const int &v) { 17 e[u].push_back(v); 18 e[v].push_back(u); 19 } 20 void dfs(const int &x,const int &par) { 21 size[x]=1; 22 dfn[x]=++dfn[0]; 23 dep[x]=dep[par]+1; 24 for(unsigned i=0;i<e[x].size();i++) { 25 const int &y=e[x][i]; 26 if(y==par) continue; 27 dfs(y,x); 28 size[x]+=size[y]; 29 } 30 } 31 class SegmentTree { 32 #define _left <<1 33 #define _right <<1|1 34 private: 35 int64 val1[N<<2],val2[N<<2]; 36 public: 37 void modify(const int &p,const int &b,const int &e,const int &l,const int &r,const int &x,const int64 &y) { 38 if(b==l&&e==r) { 39 val1[p]+=x; 40 val2[p]+=y; 41 return; 42 } 43 const int mid=(b+e)>>1; 44 if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r),x,y); 45 if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r,x,y); 46 } 47 int64 query(const int &p,const int &b,const int &e,const int &x) { 48 int64 ret=val1[p]*dep[x]+val2[p]; 49 if(b==e) return ret; 50 const int mid=(b+e)>>1; 51 if(dfn[x]<=mid) ret+=query(p _left,b,mid,x); 52 if(dfn[x]>mid) ret+=query(p _right,mid+1,e,x); 53 return ret; 54 } 55 #undef _left 56 #undef _right 57 }; 58 SegmentTree t; 59 int main() { 60 const int n=getint(),m=getint(); 61 for(register int i=1;i<=n;i++) { 62 w[i]=getint(); 63 } 64 for(register int i=1;i<n;i++) { 65 add_edge(getint(),getint()); 66 } 67 dfs(1,0); 68 for(register int i=1;i<=n;i++) { 69 t.modify(1,1,n,dfn[i],dfn[i]+size[i]-1,0,w[i]); 70 } 71 for(register int i=0;i<m;i++) { 72 const int op=getint(); 73 if(op==1) { 74 const int x=getint(),a=getint(); 75 t.modify(1,1,n,dfn[x],dfn[x]+size[x]-1,0,a); 76 } 77 if(op==2) { 78 const int x=getint(),a=getint(); 79 t.modify(1,1,n,dfn[x],dfn[x]+size[x]-1,a,a-(int64)dep[x]*a); 80 } 81 if(op==3) { 82 const int x=getint(); 83 printf("%lld\n",t.query(1,1,n,x)); 84 } 85 } 86 return 0; 87 }
同样还是一种将树“压扁”的方法。
考虑我们之前讲过的括号序列。
操作3中的询问相当于前缀和。
按照括号序列建线段树,操作1就变成了单点修改,操作2就变成了区间+a/-a。
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 typedef long long int64; 5 inline int getint() { 6 register char ch; 7 register bool neg=false; 8 while(!isdigit(ch=getchar())) if(ch=='-') neg=true; 9 register int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return neg?-x:x; 12 } 13 const int N=100001; 14 int w[N],dfn[N],size[N],dep[N]; 15 std::vector<int> e[N]; 16 inline void add_edge(const int &u,const int &v) { 17 e[u].push_back(v); 18 e[v].push_back(u); 19 } 20 void dfs(const int &x,const int &par) { 21 size[x]=1; 22 dfn[x]=++dfn[0]; 23 dep[x]=dep[par]+1; 24 for(unsigned i=0;i<e[x].size();i++) { 25 const int &y=e[x][i]; 26 if(y==par) continue; 27 dfs(y,x); 28 size[x]+=size[y]; 29 } 30 } 31 class SegmentTree { 32 #define _left <<1 33 #define _right <<1|1 34 private: 35 int64 val1[N<<2],val2[N<<2]; 36 public: 37 void modify(const int &p,const int &b,const int &e,const int &l,const int &r,const int &x,const int64 &y) { 38 if(b==l&&e==r) { 39 val1[p]+=x; 40 val2[p]+=y; 41 return; 42 } 43 const int mid=(b+e)>>1; 44 if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r),x,y); 45 if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r,x,y); 46 } 47 int64 query(const int &p,const int &b,const int &e,const int &x) { 48 int64 ret=val1[p]*dep[x]+val2[p]; 49 if(b==e) return ret; 50 const int mid=(b+e)>>1; 51 if(dfn[x]<=mid) ret+=query(p _left,b,mid,x); 52 if(dfn[x]>mid) ret+=query(p _right,mid+1,e,x); 53 return ret; 54 } 55 #undef _left 56 #undef _right 57 }; 58 SegmentTree t; 59 int main() { 60 //freopen("221/haoi2015_t21.in","r+",stdin); 61 const int n=getint(),m=getint(); 62 for(register int i=1;i<=n;i++) { 63 w[i]=getint(); 64 } 65 for(register int i=1;i<n;i++) { 66 add_edge(getint(),getint()); 67 } 68 dfs(1,0); 69 for(register int i=1;i<=n;i++) { 70 t.modify(1,1,n,dfn[i],dfn[i]+size[i]-1,0,w[i]); 71 } 72 for(register int i=0;i<m;i++) { 73 const int op=getint(); 74 if(op==1) { 75 const int x=getint(),a=getint(); 76 t.modify(1,1,n,dfn[x],dfn[x]+size[x]-1,0,a); 77 } 78 if(op==2) { 79 const int x=getint(),a=getint(); 80 t.modify(1,1,n,dfn[x],dfn[x]+size[x]-1,a,a-(int64)dep[x]*a); 81 } 82 if(op==3) { 83 const int x=getint(); 84 printf("%lld\n",t.query(1,1,n,x)); 85 } 86 } 87 return 0; 88 }