BZOJ 1036 树的统计Count 树链剖分模板题

题目链接:

https://www.lydsy.com/JudgeOnline/problem.php?id=1036

题目大意:

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作:

I. CHANGE u t : 把结点u的权值改为t

II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

思路:

树链剖分。

一直没看树链剖分,刚刚看了一下,发现不难,两个dfs预处理出轻重链,然后用线段树维护即可。

查询的时候用LCA查询。时间复杂度为两个log

推荐博文:https://www.cnblogs.com/George1994/p/7821357.html

模板:

 1 struct edge
 2 {
 3     int next;//指向下一个节点
 4     int u, v, w;
 5 };
 6 edge e[maxn];
 7 int head[maxn], node;//node记录节点的数目,head[i]记录连接着i的第一条边
 8 void addedge(int u, int v)
 9 {
10     e[node].u = u;
11     e[node].v = v;
12     //e[node].w = w;
13     e[node].next = head[u];
14     head[u] = node++;
15     e[node].u = v;
16     e[node].v = u;
17     //e[node].w = w;
18     e[node].next = head[v];
19     head[v] = node++;
20 }
21 int siz[maxn];//以u为根节点的子树的结点个数
22 int top[maxn];//节点u所在链的顶端节点
23 int son[maxn];//节点u重儿子
24 int dep[maxn];//节点u的深度
25 int faz[maxn];//节点u的父节点
26 int tid[maxn];//节点u的dfs编号
27 int rnk[maxn];//rnk[i]表示dfs编号为i的原始编号
28 int cnt;//dfs序号
29 void init()
30 {
31     memset(head, -1, sizeof(head));
32     node = 0;
33     Mem(siz);
34     Mem(top);
35     memset(son, -1, sizeof(son));
36     Mem(dep);
37     Mem(faz);
38     Mem(tid);
39     Mem(rnk);
40     cnt = 0;
41 }
42 void dfs1(int u, int father, int depth)
43 {
44     // u当前节点 father 父节点 depth深度
45     dep[u] = depth;
46     faz[u] = father;
47     siz[u] = 1;
48     for(int i = head[u]; i != -1; i = e[i].next)
49     {
50         int v = e[i].v;
51         if(v != faz[u])
52         {
53             dfs1(v, u, depth + 1);
54             siz[u] += siz[v];//更新子树节点数目
55             if(son[u] == -1 || siz[v] > siz[son[u]])
56                 son[u] = v;//更新重儿子
57         }
58     }
59 }
60 void dfs2(int u, int t)
61 {
62     //u当前节点 t起始的重节点
63     top[u] = t;
64     tid[u] = ++cnt;
65     rnk[cnt] = u;
66     if(son[u] == -1)return;//不在重链上
67     dfs2(son[u], t);//将这条重链上的所有点设置成起始的重节点
68     for(int i = head[u]; i != -1; i = e[i].next)
69     {
70         int v = e[i].v;
71         if(v != son[u] && v != faz[u])
72         {
73             dfs2(v, v);//v不是u的重儿子 也不是u的父节点 将v的top设置成自己 进一步递归
74         }
75     }
76 }

LCA的方法来查询:

 1 int solve_sum(int x, int y)//LCA top加速
 2 {
 3     int ans = 0;
 4     int fx = top[x], fy = top[y];
 5     while(fx != fy)
 6     {
 7         if(dep[fx] >= dep[fy])
 8         {
 9             //计算x到其链的起点的路径和
10             ans += query_sum(1, tid[fx], tid[x]);
11             //将x设置成起始节点的父节点,走轻边,继续循环
12             x = faz[fx];
13         }
14         else
15         {
16             ans += query_sum(1, tid[fy], tid[y]);
17             y = faz[fy];
18         }
19         fx = top[x], fy = top[y];
20     }
21     //此时x和y在同一条重链上
22     if(tid[x] <= tid[y])ans += query_sum(1, tid[x], tid[y]);
23     else ans += query_sum(1, tid[y], tid[x]);
24     return ans;
25 }

 

题目完整代码:

  1 #include<bits/stdc++.h>
  2 #define IOS ios::sync_with_stdio(false);//不可再使用scanf printf
  3 #define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数,会超时
  4 #define Min(a, b) ((a) < (b) ? (a) : (b))
  5 #define Mem(a) memset(a, 0, sizeof(a))
  6 #define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1))
  7 #define MID(l, r) ((l) + ((r) - (l)) / 2)
  8 #define lson ((o)<<1)
  9 #define rson ((o)<<1|1)
 10 #pragma comment(linker, "/STACK:102400000,102400000")//栈外挂
 11 using namespace std;
 12 inline int read()
 13 {
 14     int x=0,f=1;char ch=getchar();
 15     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
 16     while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
 17     return x*f;
 18 }
 19 
 20 typedef long long ll;
 21 const int maxn = 200000 + 10;
 22 const int mod = 1000000007;//const引用更快,宏定义也更快
 23 const int INF = 1e9;
 24 
 25 struct edge
 26 {
 27     int next;//指向下一个节点
 28     int u, v, w;
 29 };
 30 edge e[maxn];
 31 int head[maxn], node;//node记录节点的数目,head[i]记录连接着i的第一条边
 32 void addedge(int u, int v)
 33 {
 34     e[node].u = u;
 35     e[node].v = v;
 36     //e[node].w = w;
 37     e[node].next = head[u];
 38     head[u] = node++;
 39     e[node].u = v;
 40     e[node].v = u;
 41     //e[node].w = w;
 42     e[node].next = head[v];
 43     head[v] = node++;
 44 }
 45 int siz[maxn];//以u为根节点的子树的结点个数
 46 int top[maxn];//节点u所在链的顶端节点
 47 int son[maxn];//节点u重儿子
 48 int dep[maxn];//节点u的深度
 49 int faz[maxn];//节点u的父节点
 50 int tid[maxn];//节点u的dfs编号
 51 int rnk[maxn];//rnk[i]表示dfs编号为i的原始编号
 52 int cnt;//dfs序号
 53 void init()
 54 {
 55     memset(head, -1, sizeof(head));
 56     node = 0;
 57     Mem(siz);
 58     Mem(top);
 59     memset(son, -1, sizeof(son));
 60     Mem(dep);
 61     Mem(faz);
 62     Mem(tid);
 63     Mem(rnk);
 64     cnt = 0;
 65 }
 66 void dfs1(int u, int father, int depth)
 67 {
 68     // u当前节点 father 父节点 depth深度
 69     dep[u] = depth;
 70     faz[u] = father;
 71     siz[u] = 1;
 72     for(int i = head[u]; i != -1; i = e[i].next)
 73     {
 74         int v = e[i].v;
 75         if(v != faz[u])
 76         {
 77             dfs1(v, u, depth + 1);
 78             siz[u] += siz[v];//更新子树节点数目
 79             if(son[u] == -1 || siz[v] > siz[son[u]])
 80                 son[u] = v;//更新重儿子
 81         }
 82     }
 83 }
 84 void dfs2(int u, int t)
 85 {
 86     //u当前节点 t起始的重节点
 87     top[u] = t;
 88     tid[u] = ++cnt;
 89     rnk[cnt] = u;
 90     if(son[u] == -1)return;//不在重链上
 91     dfs2(son[u], t);//将这条重链上的所有点设置成起始的重节点
 92     for(int i = head[u]; i != -1; i = e[i].next)
 93     {
 94         int v = e[i].v;
 95         if(v != son[u] && v != faz[u])
 96         {
 97             dfs2(v, v);//v不是u的重儿子 也不是u的父节点 将v的top设置成自己 进一步递归
 98         }
 99     }
100 }
101 struct node{
102     int l, r, x, sum;
103 }tree[maxn];
104 int value[maxn];
105 void build(int o, int l, int r)
106 {
107     tree[o].l = l, tree[o].r = r;
108     if(l == r)
109     {
110         tree[o].x = tree[o].sum = value[rnk[l]];//rnk[i]表示dfs编号为i,原始编号为rnk[i]
111         return;
112     }
113     int m = MID(l, r);
114     build(lson, l, m);
115     build(rson, m + 1, r);
116     tree[o].sum = tree[lson].sum + tree[rson].sum;
117     tree[o].x = Max(tree[lson].x, tree[rson].x);
118 }
119 void update(int o, int p, int v)
120 {
121     if(tree[o].l == tree[o].r){tree[o].sum = tree[o].x = v;return;}
122     if(p <= tree[lson].r)update(lson, p, v);
123     else update(rson, p, v);
124     tree[o].sum = tree[lson].sum + tree[rson].sum;
125     tree[o].x = Max(tree[lson].x, tree[rson].x);
126 }
127 int query_sum(int o, int ql, int qr)
128 {
129     if(ql <= tree[o].l && qr >= tree[o].r)return tree[o].sum;
130     int ans = 0;
131     if(ql <= tree[lson].r)ans += query_sum(lson, ql, qr);
132     if(qr >= tree[rson].l)ans += query_sum(rson, ql, qr);
133     return ans;
134 }
135 int query_max(int o, int ql, int qr)
136 {
137     //cout<<o<<endl;
138     if(ql <= tree[o].l && qr >= tree[o].r)return tree[o].x;
139     int ans = -INF, tmp;
140     if(ql <= tree[lson].r)tmp = query_max(lson, ql, qr), ans = Max(ans, tmp);
141     if(qr >= tree[rson].l)tmp = query_max(rson, ql, qr), ans = Max(ans, tmp);
142     return ans;
143 }
144 int solve_sum(int x, int y)//LCA top加速
145 {
146     int ans = 0;
147     int fx = top[x], fy = top[y];
148     while(fx != fy)
149     {
150         if(dep[fx] >= dep[fy])
151         {
152             //计算x到其链的起点的路径和
153             ans += query_sum(1, tid[fx], tid[x]);
154             //将x设置成起始节点的父节点,走轻边,继续循环
155             x = faz[fx];
156         }
157         else
158         {
159             ans += query_sum(1, tid[fy], tid[y]);
160             y = faz[fy];
161         }
162         fx = top[x], fy = top[y];
163     }
164     //此时x和y在同一条重链上
165     if(tid[x] <= tid[y])ans += query_sum(1, tid[x], tid[y]);
166     else ans += query_sum(1, tid[y], tid[x]);
167     return ans;
168 }
169 int solve_max(int x, int y)//LCA top加速
170 {
171     int ans = -INF, tmp;
172     int fx = top[x], fy = top[y];
173     while(fx != fy)
174     {
175         if(dep[fx] >= dep[fy])
176         {
177             //计算x到其链的起点的路径和
178             tmp = query_max(1, tid[fx], tid[x]);
179             //将x设置成起始节点的父节点,走轻边,继续循环
180             x = faz[fx];
181         }
182         else
183         {
184             tmp = query_max(1, tid[fy], tid[y]);
185             y = faz[fy];
186         }
187         ans = Max(ans, tmp);
188         fx = top[x], fy = top[y];
189     }
190     //此时x和y在同一条重链上
191     if(tid[x] <= tid[y])tmp = query_max(1, tid[x], tid[y]);
192     else tmp = query_max(1, tid[y], tid[x]);
193     ans = Max(ans, tmp);
194     return ans;
195 }
196 
197 int main()
198 {
199     init();
200     int n, u, v;
201     scanf("%d", &n);
202     for(int i = 1; i < n; i++)
203     {
204         scanf("%d%d", &u, &v);
205         addedge(u, v);
206     }
207     for(int i = 1; i <= n; i++)scanf("%d", &value[i]);
208     dfs1(1, 0, 1);
209     dfs2(1, 1);
210     build(1, 1, n);
211     int m, a, b;
212     char s[10];
213     scanf("%d", &m);
214     while(m--)
215     {
216         scanf("%s%d%d", s, &a, &b);
217         if(s[0] == 'C')update(1, tid[a], b);//更新的时候下标必须使用dfs编号的
218         else if(s[1] == 'S')printf("%d\n", solve_sum(a, b));
219         else printf("%d\n", solve_max(a, b));
220     }
221     return 0;
222 }

 

posted @ 2018-09-10 23:14  _努力努力再努力x  阅读(191)  评论(0编辑  收藏  举报