[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
",query(x));
134         }
135     }
136     return 0;
137 }
View Code

  题目求的是点到根路径上的权值和,那么修改单点对整棵子树所有结点的询问都有贡献。
  给整棵子树增加权值显然也会对整棵子树的询问产生贡献。
  若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
",t.query(1,1,n,x));
84         }
85     }
86     return 0;
87 }
View Code

  同样还是一种将树“压扁”的方法。
  考虑我们之前讲过的括号序列。
  操作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
",t.query(1,1,n,x));
85         }
86     }
87     return 0;
88 }
View Code
原文地址:https://www.cnblogs.com/skylee03/p/8066974.html