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 }
越努力,越幸运