树上倍增、树分治学习总结
树上倍增:
核心:
F[u][i] 表示u节点往上走2i步到达的祖先节点,F[u][0]=fa[u],F[u][i]=F[F[u][i-1]][i-1],其他需要维护的数值也与此格式类似
【SCOI2016】幸运数字(题目):
倍增时合并一下线性基即可
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 typedef long long LL; 8 9 const int N = 20010; 10 11 int n, q; 12 LL G[N], dep[N], ans[N]; 13 LL fa[N][21], p[N][21][62]; 14 int h[N], num[N << 1], nex[N << 1], dqx; 15 16 inline void add(int a, int b) 17 { 18 num[dqx] = b; 19 nex[dqx] = h[a]; 20 h[a] = dqx++; 21 } 22 23 inline void get_p(LL *a, LL val) 24 { 25 for (int i = 61; i >= 0; i--) 26 { 27 if ((val >> i) & 1) 28 { 29 if (!a[i]) 30 { 31 a[i] = val; 32 break; 33 } 34 val ^= a[i]; 35 } 36 } 37 } 38 39 void dfs(int u, int f) 40 { 41 fa[u][0] = f; 42 dep[u] = dep[f] + 1; 43 for (int i = h[u]; ~i; i = nex[i]) 44 { 45 int j = num[i]; 46 if (j == f) continue; 47 dfs(j, u); 48 } 49 } 50 51 void merge(LL* a, LL* b) 52 { 53 for (int i = 61; i >= 0; i--) 54 { 55 if (b[i]) get_p(a, b[i]); 56 } 57 } 58 59 void get_lca() 60 { 61 for (int j = 1; j < 20; j++) 62 { 63 for (int i = 1; i <= n; i++) 64 { 65 fa[i][j] = fa[fa[i][j - 1]][j - 1]; 66 memcpy(p[i][j], p[i][j - 1], sizeof(p[i][j - 1])); 67 merge(p[i][j], p[fa[i][j - 1]][j - 1]); 68 } 69 } 70 } 71 72 inline void LCA(int u, int v) 73 { 74 if (dep[u] < dep[v]) swap(u, v); 75 76 for (int i = 20; i >= 0; i--) 77 { 78 if (dep[fa[u][i]] >= dep[v]) 79 { 80 merge(ans, p[u][i]); 81 u = fa[u][i]; 82 } 83 } 84 85 if (u == v) 86 { 87 merge(ans, p[u][0]); 88 return; 89 } 90 91 for (int i = 20; i >= 0; i--) 92 { 93 if (fa[u][i] != fa[v][i]) 94 { 95 merge(ans, p[u][i]), merge(ans, p[v][i]); 96 u = fa[u][i], v = fa[v][i]; 97 } 98 } 99 100 merge(ans, p[u][0]); 101 merge(ans, p[v][0]); 102 merge(ans, p[fa[u][0]][0]); 103 } 104 105 int main() 106 { 107 scanf("%d%d", &n, &q); 108 for (int i = 1; i <= n; i++) 109 { 110 scanf("%lld", &G[i]); 111 get_p(p[i][0], G[i]); 112 } 113 114 memset(h, -1, sizeof(h)); 115 for (int i = 1; i <= n - 1; i++) 116 { 117 int a, b; 118 scanf("%d%d", &a, &b); 119 add(a, b), add(b, a); 120 } 121 122 dfs(1, 0); 123 get_lca(); 124 125 for (int i = 1; i <= q; i++) 126 { 127 memset(ans, 0, sizeof(ans)); 128 129 int a, b; 130 scanf("%d%d", &a, &b); 131 LCA(a, b); 132 LL res = 0; 133 134 for (int j = 61; j >= 0; j--) res = max(res, res ^ (LL)ans[j]); 135 printf("%lld\n", res); 136 } 137 }
【NOI2018】归程(题目):
进行一次Dijkstra,算出每个点到根(1号点)的最短距离dis,再把每条边按照海拔进行排序后进行kruskal重构树,重构树中的每个节点存储的是它代表的边的海拔以及他的子树中最小的dis(相当于是距离1最近的点的距离)。求值时倍增上去,第一个上不去的点的dis就是答案。
ps:关于kurskal重构树
树中除了叶节点是原树的点外其他的节点都代表原树的边,当要连接两个点时,将两个点所属的并查集的最高点合并,这两个最高点的父节点便变成代替当前这条边的节点。
如图,假如有一条编号为6的边要连接1,3两个点,则将1和5的父节点变成6(图中的1,2,3皆为点,其余为边)。
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 typedef pair<int, int> PII; 9 10 const int N = 2000010; 11 12 int n, m; 13 int fa[N]; 14 int dis[N]; 15 int dep[N], F[N][20]; 16 int h1[N], num1[N], nex1[N], dqx1; 17 int h[N], num[N], nex[N], v[N], dqx; 18 bool st[N]; 19 20 struct Edge 21 { 22 int a, b, l, v; 23 Edge() {} 24 Edge(int a, int b, int l, int v) : a(a), b(b), l(l), v(v) {} 25 }; 26 27 Edge edge[N << 2], p[N << 2]; 28 29 void add(int a, int b, int c) 30 { 31 num[dqx] = b; 32 v[dqx] = c; 33 nex[dqx] = h[a]; 34 h[a] = dqx++; 35 } 36 37 void Dijkstra() 38 { 39 memset(st, 0, sizeof(st)); 40 memset(dis, 0x3f, sizeof(dis)); 41 42 priority_queue<PII, vector<PII>, greater<PII> > que; 43 dis[1] = 0; 44 que.push(make_pair(dis[1], 1)); 45 46 while (!que.empty()) 47 { 48 PII t = que.top(); 49 que.pop(); 50 int u = t.second, d = t.first; 51 52 if (st[u]) continue; 53 st[u] = true; 54 55 for (int i = h[u]; ~i; i = nex[i]) 56 { 57 int j = num[i]; 58 if (st[j]) continue; 59 if (dis[j] > dis[u] + v[i]) 60 { 61 dis[j] = dis[u] + v[i]; 62 que.push(make_pair(dis[j], j)); 63 } 64 } 65 } 66 } 67 68 bool cmp(Edge a, Edge b) 69 { 70 return a.v > b.v; 71 } 72 73 void addx(int a, int b) 74 { 75 num1[dqx1] = b; 76 nex1[dqx1] = h1[a]; 77 h1[a] = dqx1++; 78 } 79 80 void dfs(int u, int f) 81 { 82 dep[u] = dep[f] + 1; 83 F[u][0] = f; 84 85 for (int i = 1; i <= 19; i++) 86 { 87 F[u][i] = F[F[u][i - 1]][i - 1]; 88 } 89 90 for (int i = h1[u]; ~i; i = nex1[i]) 91 { 92 int j = num1[i]; 93 if (j == f) continue; 94 dfs(j, u); 95 p[u].l = min(p[u].l, p[j].l); 96 } 97 } 98 99 int find1(int x) 100 { 101 return fa[x] == x ? x : fa[x] = find1(fa[x]); 102 } 103 104 void kruskal() 105 { 106 int tot = 0, cnt = n; 107 108 for (int i = 1; i <= (n << 1); i++) fa[i] = i; 109 sort(edge + 1, edge + m + 1, cmp); 110 111 memset(h1, -1, sizeof(h1)); 112 for (int i = 1; i <= m; i++) 113 { 114 int a = edge[i].a, b = edge[i].b; 115 int fx = find1(a), fy = find1(b); 116 if (fx == fy) continue; 117 118 addx(++cnt, fx), addx(cnt, fy); 119 120 fa[fx] = cnt, fa[fy] = cnt; 121 122 p[cnt].v = edge[i].v; 123 tot++; 124 if (tot == n - 1) break; 125 } 126 127 dfs(cnt, 0); 128 } 129 130 int query(int a, int v) 131 { 132 for (int i = 19; i >= 0; i--) if (dep[a] - (1 << i) > 0 && p[F[a][i]].v > v) a = F[a][i]; 133 return p[a].l; 134 } 135 136 void init() 137 { 138 dqx = dqx1 = 0; 139 memset(edge, 0, sizeof(edge)); 140 memset(h, -1, sizeof(h)); 141 memset(h1, -1, sizeof(h1)); 142 memset(F, 0, sizeof(F)); 143 } 144 145 int main() 146 { 147 int T; 148 scanf("%d", &T); 149 while (T--) 150 { 151 init(); 152 153 scanf("%d%d", &n, &m); 154 155 for (int i = 1; i <= m; i++) 156 { 157 int a, b, l, v; 158 scanf("%d%d%d%d", &a, &b, &l, &v); 159 edge[i] = Edge(a, b, l, v); 160 add(a, b, l), add(b, a, l); 161 } 162 163 for (int i = n + 1; i <= n * 2; i++) p[i].l = 2e9 + 7; 164 165 Dijkstra(); 166 for (int i = 1; i <= n; i++) p[i].l = dis[i]; 167 168 kruskal(); 169 170 int ans = 0; 171 int q, k, s; 172 scanf("%d%d%d", &q, &k, &s); 173 while (q--) 174 { 175 int u0, p0; 176 scanf("%d%d", &u0, &p0); 177 u0 = (u0 + k * ans - 1) % n + 1; 178 p0 = (p0 + k * ans) % (s + 1); 179 180 ans = query(u0, p0); 181 printf("%d\n", ans); 182 } 183 184 } 185 }
【美团 CodeM 复赛】城市网络(题目):
对于每次询问,新建一个节点,与u相连,权值为c。之后进行dfs,每个节点的父节点定为深度最深的权值比他大的点,求答案时从该询问对应新建的节点网上倍增,每次倍增时答案加上倍增往上跳的节点数。
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int N = 200010; 9 10 int n, m; 11 int k, t; 12 int a[N], dep[N], to[N], F[20][N]; 13 14 vector<int> e[N]; 15 16 void dfs(int x, int f) 17 { 18 int pos = f; 19 for (int i = 19; i >= 0; i--) 20 { 21 if (F[i][pos] && a[F[i][pos]] <= a[x]) 22 { 23 pos = F[i][pos]; 24 } 25 } 26 27 if (a[pos] > a[x]) F[0][x] = pos; 28 else F[0][x] = F[0][pos]; 29 30 for (int i = 1; F[i - 1][F[i - 1][x]]; i++) 31 { 32 F[i][x] = F[i - 1][F[i - 1][x]]; 33 } 34 dep[x] = dep[f] + 1; 35 36 for (int i : e[x]) 37 { 38 if (i == f)continue; 39 dfs(i, x); 40 } 41 } 42 43 int main() 44 { 45 int q; 46 scanf("%d%d", &n, &q); 47 48 for (int i = 1; i <= n; i++) scanf("%d", &a[i]); 49 for (int i = 1; i < n; i++) 50 { 51 int a, b; 52 scanf("%d%d", &a, &b); 53 e[a].push_back(b), e[b].push_back(a); 54 } 55 56 for (int i = 1; i <= q; i++) 57 { 58 int x, y, z; 59 scanf("%d%d%d", &x, &y, &z); 60 a[n + i] = z; 61 e[n + i].push_back(x); 62 e[x].push_back(n + i); 63 to[n + i] = y; 64 } 65 66 dfs(1, 0); 67 68 for (int i = n + 1; i <= n + q; i++) 69 { 70 int ret = 0, pos = i; 71 for (int j = 19; j >= 0; j--) 72 { 73 if (dep[F[j][pos]] >= dep[to[i]]) 74 { 75 ret += (1 << j); 76 pos = F[j][pos]; 77 } 78 } 79 printf("%d\n", ret); 80 } 81 return 0; 82 }
【BJWC2010】严格次小生成树(题目):
倍增数组多存储一个边的次大值,之后枚举边时若和最大值相同就比较次大值。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 typedef long long LL; 8 9 const int N = 500010; 10 const LL INF = 2e18 + 7; 11 12 int n, m; 13 int F[N][20], dep[N], fa[N]; 14 LL ma[N][20], mi[N][20]; 15 int h[N], num[N], nex[N], v[N], dqx; 16 bool st[N]; 17 18 struct Edge 19 { 20 int a, b, d; 21 Edge() {} 22 }; 23 24 Edge e[N]; 25 26 bool cmp(Edge a, Edge b) 27 { 28 return a.d < b.d; 29 } 30 31 void add(int a, int b, int c) 32 { 33 num[dqx] = b; 34 v[dqx] = c; 35 nex[dqx] = h[a]; 36 h[a] = dqx++; 37 } 38 39 int find1(int x) 40 { 41 return fa[x] == x ? x : fa[x] = find1(fa[x]); 42 } 43 44 void dfs(int u, int pa) 45 { 46 F[u][0] = pa; 47 dep[u] = dep[pa] + 1; 48 49 for (int i = h[u]; ~i; i = nex[i]) 50 { 51 int j = num[i]; 52 if (j == pa) continue; 53 ma[j][0] = v[i]; 54 mi[j][0] = -INF; 55 dfs(j, u); 56 } 57 } 58 59 LL query_max(int a, int b, int c) 60 { 61 LL ans = -INF; 62 for (int i = 18; i >= 0; --i) 63 { 64 if (dep[F[a][i]] >= dep[b]) 65 { 66 if (c != ma[a][i]) ans = max(ans, ma[a][i]); 67 else ans = max(ans, mi[a][i]); 68 a = F[a][i]; 69 } 70 } 71 return ans; 72 } 73 74 int LCA(int a, int b) 75 { 76 if (dep[a] < dep[b]) swap(a, b); 77 78 for (int i = 18; i >= 0; i--) 79 { 80 if (dep[F[a][i]] >= dep[b]) a = F[a][i]; 81 } 82 if (a == b) return a; 83 84 for (int i = 18; i >= 0; i--) 85 { 86 if (F[a][i] != F[b][i]) 87 { 88 a = F[a][i]; 89 b = F[b][i]; 90 } 91 } 92 93 return F[a][0]; 94 } 95 96 int main() 97 { 98 scanf("%d%d", &n, &m); 99 for (int i = 1; i <= m; i++) 100 { 101 scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].d); 102 } 103 104 sort(e + 1, e + 1 + m, cmp); 105 106 LL res = 0; 107 memset(h, -1, sizeof(h)); 108 for (int i = 1; i <= n; i++) fa[i] = i; 109 for (int i = 1; i <= m; i++) 110 { 111 int a = e[i].a, b = e[i].b, d = e[i].d; 112 int fx = find1(a), fy = find1(b); 113 114 if (fx == fy) continue; 115 fa[fx] = fy; 116 add(a, b, d); 117 add(b, a, d); 118 res += d; 119 120 st[i] = true; 121 } 122 123 mi[1][0] = -INF; 124 dfs(1, 0); 125 126 for (int i = 1; i <= 18; ++i) 127 { 128 for (int j = 1; j <= n; ++j) 129 { 130 F[j][i] = F[F[j][i - 1]][i - 1]; 131 ma[j][i] = max(ma[j][i - 1], ma[F[j][i - 1]][i - 1]); 132 mi[j][i] = max(mi[j][i - 1], mi[F[j][i - 1]][i - 1]); 133 if (ma[j][i - 1] > ma[F[j][i - 1]][i - 1]) mi[j][i] = max(mi[j][i], ma[F[j][i - 1]][i - 1]); 134 else if (ma[j][i - 1] < ma[F[j][i - 1]][i - 1]) mi[j][i] = max(mi[j][i], ma[j][i - 1]); 135 } 136 } 137 138 LL ans = INF; 139 for (int i = 1; i <= m; i++) 140 { 141 if (st[i]) continue; 142 143 int a = e[i].a, b = e[i].b, d = e[i].d; 144 145 int lca = LCA(a, b); 146 LL m1 = query_max(a, lca, d); 147 LL m2 = query_max(b, lca, d); 148 ans = min(ans, res - max(m1, m2) + d); 149 } 150 151 printf("%lld", ans); 152 }
【CF519E】A and B and Lecture Rooms(题目):
如图,若a点为16,b点为15,先求出他们的LCA,为2,这时候a到b的路径的中点为5,答案就是size[5]-size[7]。
若a点为16,b点为8,LCA为2,这时候中点就是LCA,答案就是n-size[4]-size[5]。
若两点的深度差为奇数,则无解。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 200010; 8 9 int n; 10 int F[N][20]; 11 int size1[N], dep[N]; 12 int h[N], num[N], nex[N], dqx; 13 14 void add(int a, int b) 15 { 16 num[dqx] = b; 17 nex[dqx] = h[a]; 18 h[a] = dqx++; 19 } 20 21 void dfs(int u, int pa) 22 { 23 size1[u] = 1; 24 dep[u] = dep[pa] + 1; 25 26 F[u][0] = pa; 27 for (int i = 1; i <= 18; i++) F[u][i] = F[F[u][i - 1]][i - 1]; 28 29 for (int i = h[u]; ~i; i = nex[i]) 30 { 31 int j = num[i]; 32 if (j == pa) continue; 33 dfs(j, u); 34 size1[u] += size1[j]; 35 } 36 } 37 38 int LCA(int a, int b) 39 { 40 if (a == b) return n; 41 if (dep[a] == dep[b]) 42 { 43 for (int i = 18; i >= 0; i--) 44 { 45 if (F[a][i] != F[b][i]) 46 { 47 a = F[a][i]; 48 b = F[b][i]; 49 } 50 } 51 return n - size1[a] - size1[b]; 52 } 53 54 if (dep[a] < dep[b]) swap(a, b); 55 56 if ((dep[a] - dep[b]) % 2) return 0; 57 58 int x = a, d = (dep[a] - dep[b]) / 2; 59 for (int i = 18; i >= 0; i--) 60 { 61 if (dep[F[a][i]] >= dep[b]) a = F[a][i]; 62 } 63 64 if (a == b) 65 { 66 d = dep[a] + d; 67 for (int i = 18; i >= 0; i--) 68 { 69 if (dep[F[x][i]] > d) x = F[x][i]; 70 } 71 return size1[F[x][0]] - size1[x]; 72 } 73 74 for (int i = 18; i >= 0; i--) 75 { 76 if (F[a][i] != F[b][i]) 77 { 78 a = F[a][i], b = F[b][i]; 79 } 80 } 81 82 d = dep[a] - 1 + d; 83 for (int i = 18; i >= 0; i--) 84 { 85 if (dep[F[x][i]] > d) x = F[x][i]; 86 } 87 88 return size1[F[x][0]] - size1[x]; 89 } 90 91 int main() 92 { 93 scanf("%d", &n); 94 95 memset(h, -1, sizeof(h)); 96 for (int i = 1; i < n; i++) 97 { 98 int a, b; 99 scanf("%d%d", &a, &b); 100 add(a, b); 101 add(b, a); 102 } 103 104 dfs(1, 0); 105 106 int m; 107 scanf("%d", &m); 108 while (m--) 109 { 110 int a, b; 111 scanf("%d%d", &a, &b); 112 printf("%d\n", LCA(a, b)); 113 } 114 }
【ZJOI2012】灾难(题目):
正反向图都建一个,然后用反向图拓扑排序,这样就能使拓扑排序中每个动物的食物都在它的前面。再遍历拓扑序,用正向图求出当前节点代表的动物的食物的公共祖先,因为只有所有的食物的公共祖先消失后,它的食物才会全部消失,它才会消失。再将它的所有食物的公共祖先向它连边,最后dfs遍历求出每个节点的子树大小,子树大小减一便是灾难值。
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int N = 2000010; 9 10 int n; 11 int ran[N]; 12 int ind[N], dep[N], size1[N], F[N][20]; 13 int h[N], num[N], nex[N], dqx; 14 int h1[N], num1[N], nex1[N], dqx1; 15 int h2[N], num2[N], nex2[N], dqx2; 16 17 void add(int a, int b) 18 { 19 num[++dqx] = b; 20 nex[dqx] = h[a]; 21 h[a] = dqx; 22 } 23 24 void add1(int a, int b) 25 { 26 num1[++dqx1] = b; 27 nex1[dqx1] = h1[a]; 28 h1[a] = dqx1; 29 } 30 31 void add2(int a, int b) 32 { 33 num2[++dqx2] = b; 34 nex2[dqx2] = h2[a]; 35 h2[a] = dqx2; 36 } 37 38 void topsort() 39 { 40 queue<int> que; 41 for (int i = 1; i <= n; i++) 42 { 43 if (!ind[i]) que.push(i); 44 } 45 46 int cnt = 0; 47 while (!que.empty()) 48 { 49 int u = que.front(); 50 que.pop(); 51 ran[++cnt] = u; 52 53 for (int i = h[u]; i; i = nex[i]) 54 { 55 int j = num[i]; 56 if (--ind[j] == 0) que.push(j); 57 } 58 } 59 } 60 61 int LCA(int a, int b) 62 { 63 if (dep[a] < dep[b]) swap(a, b); 64 65 for (int i = 18; i >= 0; i--) 66 { 67 if (dep[F[a][i]] >= dep[b]) a = F[a][i]; 68 } 69 70 if (a == b) return a; 71 72 for (int i = 18; i >= 0; i--) 73 { 74 if (F[a][i] != F[b][i]) 75 { 76 a = F[a][i]; 77 b = F[b][i]; 78 } 79 } 80 81 return F[a][0]; 82 } 83 84 void dfs(int u) 85 { 86 size1[u] = 1; 87 for (int i = h2[u]; i; i = nex2[i]) 88 { 89 int j = num2[i]; 90 dfs(j); 91 size1[u] += size1[j]; 92 } 93 } 94 95 int main() 96 { 97 scanf("%d", &n); 98 99 for (int i = 1; i <= n; i++) 100 { 101 int x, a; 102 while (scanf("%d", &x), x) 103 { 104 ind[i]++; 105 add(x, i); 106 add1(i, x); 107 } 108 } 109 110 topsort(); 111 112 for (int i = 1; i <= n; i++) 113 { 114 int u = num1[h1[ran[i]]]; 115 for (int j = h1[ran[i]]; j; j = nex1[j]) u = LCA(u, num1[j]); 116 117 add2(u, ran[i]); 118 dep[ran[i]] = dep[u] + 1; 119 F[ran[i]][0] = u; 120 for (int j = 1; j <= 18; j++) F[ran[i]][j] = F[F[ran[i]][j - 1]][j - 1]; 121 } 122 123 dfs(0); 124 125 for (int i = 1; i <= n; i++) printf("%d\n", size1[i] - 1); 126 }
【洛谷P4197】Peaks(题目):
kruskal重建树,记录每个节点的子树区间左端点右端点(range),然后用主席树来求第k大数。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 6000010; 8 9 int n, m; 10 int cnt, tot; 11 int w[N]; 12 int fa[N], F[N][20]; 13 int b[N], lenb; 14 int range[N][2], dis[N]; 15 int rt[N], ls[N], rs[N], sum[N]; 16 int h[N], num[N], nex[N], dqx; 17 18 struct Edge 19 { 20 int a, b, d; 21 Edge() {} 22 Edge(int a, int b, int d) :a(a), b(b), d(d) {} 23 }; 24 25 Edge e[N]; 26 27 bool cmp(Edge a, Edge b) 28 { 29 return a.d < b.d; 30 } 31 32 void add(int a, int b) 33 { 34 num[dqx] = b; 35 nex[dqx] = h[a]; 36 h[a] = dqx++; 37 } 38 39 void build(int& q, int l, int r) 40 { 41 q = ++cnt; 42 if (l == r) return; 43 int mid = (l + r) >> 1; 44 build(ls[q], l, mid); 45 build(rs[q], mid + 1, r); 46 } 47 48 void modify(int pre, int& q, int l, int r, int k) 49 { 50 q = ++cnt; 51 ls[q] = ls[pre], rs[q] = rs[pre], sum[q] = sum[pre] + 1; 52 if (l == r) return; 53 int mid = (l + r) >> 1; 54 if (k <= mid) modify(ls[pre], ls[q], l, mid, k); 55 else modify(rs[pre], rs[q], mid + 1, r, k); 56 } 57 58 int query(int pre, int q, int l, int r, int k) 59 { 60 if (l == r) return l; 61 int mid = (l + r) >> 1; 62 int d = sum[rs[q]] - sum[rs[pre]]; 63 if (k > d) return query(ls[pre], ls[q], l, mid, k-d); // 因为要求第k高,但主席树的值是从小到大的,所以要反着来 64 else return query(rs[pre], rs[q], mid + 1, r, k); 65 } 66 67 void dfs(int u) 68 { 69 for (int i = 1; i <= 18; i++) F[u][i] = F[F[u][i - 1]][i - 1]; 70 71 range[u][0] = tot; 72 if (!(~h[u])) 73 { 74 int k = lower_bound(b + 1, b + lenb + 1, w[u]) - b; 75 range[u][0] = ++tot; 76 modify(rt[tot - 1], rt[tot], 1, lenb, k); 77 return; 78 } 79 80 for (int i = h[u]; ~i; i = nex[i]) 81 { 82 dfs(num[i]); 83 } 84 85 range[u][1] = tot; 86 } 87 88 int find1(int x) 89 { 90 return fa[x] == x ? x : fa[x] = find1(fa[x]); 91 } 92 93 void kruskal() 94 { 95 sort(e + 1, e + 1 + m, cmp); 96 for (int i = 1; i <= n; i++) fa[i] = i; 97 98 int tmp = n; 99 memset(h, -1, sizeof(h)); 100 for (int i = 1; i <= m; i++) 101 { 102 int x = e[i].a, y = e[i].b; 103 int fx = find1(x), fy = find1(y); 104 if (fx == fy) continue; 105 106 fa[fx] = ++tmp; 107 fa[fy] = tmp; 108 fa[tmp] = tmp; 109 110 add(tmp, fx), add(tmp, fy); 111 F[fx][0] = F[fy][0] = tmp; 112 dis[tmp] = e[i].d; 113 } 114 115 build(rt[0], 1, lenb); 116 dfs(tmp); 117 } 118 119 int main() 120 { 121 int q; 122 scanf("%d%d%d", &n, &m, &q); 123 for (int i = 1; i <= n; i++) 124 { 125 scanf("%d", &w[i]); 126 b[i] = w[i]; 127 } 128 129 sort(b + 1, b + n + 1); 130 lenb = unique(b + 1, b + 1 + n) - b - 1; 131 132 for (int i = 1; i <= m; i++) 133 { 134 int x, y, z; 135 scanf("%d%d%d", &x, &y, &z); 136 e[i] = Edge(x, y, z); 137 } 138 139 kruskal(); 140 141 while (q--) 142 { 143 int x, d, k; 144 scanf("%d%d%d", &x, &d, &k); 145 for (int i = 18; i >= 0; i--) 146 { 147 if (F[x][i] && dis[F[x][i]] <= d) x = F[x][i]; 148 } 149 if (sum[rt[range[x][1]]] - sum[rt[range[x][0]]] < k) puts("-1"); 150 else printf("%d\n", b[query(rt[range[x][0]], rt[range[x][1]], 1, lenb, k)]); 151 } 152 }
【PA2014】Fiolki(题目):
类似于kurskal重构树,每次混合药剂后,新建一个节点,表示混合后的bi瓶子,新节点分别是ai,bi的父节点,下次需要用到bi来混合时就要用这个新建节点来加边。
反应发生的位置就是会发生反应的两个药剂的LCA,将反应按照LCA深度从大到小为第一关键字,编号从小到大为第二关键字排序,没有LCA的反应踢掉(没有一次混合会发生这个反应)。遍历一遍反应,将反应需要的两种药剂都减去两种药剂中剂量较少的药剂的剂量,答案加上较少药剂的剂量*2。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 7 const int N = 2000010; 8 9 int n, m; 10 int w[N], pos[N]; 11 int dep[N], F[N][20]; 12 int h[N], num[N], nex[N], dqx; 13 14 struct Node 15 { 16 int a, b, dep, id; 17 Node() {} 18 Node(int a, int b, int dep, int id) :a(a), b(b), dep(dep), id(id) {} 19 }; 20 21 Node rea[N]; 22 23 bool cmp(Node a, Node b) 24 { 25 return a.dep == b.dep ? a.id < b.id : a.dep > b.dep; 26 } 27 28 void add(int a, int b) 29 { 30 num[dqx] = b; 31 nex[dqx] = h[a]; 32 h[a] = dqx++; 33 } 34 35 void dfs(int u) 36 { 37 dep[u] = dep[F[u][0]] + 1; 38 39 for (int i = 1; i <= 18; i++) F[u][i] = F[F[u][i - 1]][i - 1]; 40 41 for (int i = h[u]; ~i; i = nex[i]) 42 { 43 int j = num[i]; 44 if (j == F[u][0]) continue; 45 F[j][0] = u; 46 dfs(j); 47 } 48 } 49 50 int LCA(int a, int b) 51 { 52 if (dep[a] < dep[b]) swap(a, b); 53 for (int i = 18; i >= 0; i--) 54 { 55 if (dep[F[a][i]] >= dep[b]) a = F[a][i]; 56 } 57 58 if (a == b) return a; 59 60 for (int i = 18; i >= 0; i--) 61 { 62 if (F[a][i] != F[b][i]) 63 { 64 a = F[a][i]; 65 b = F[b][i]; 66 } 67 } 68 69 return F[a][0]; 70 } 71 72 int main() 73 { 74 int q; 75 scanf("%d%d%d", &n, &m, &q); 76 for (int i = 1; i <= n; i++) 77 { 78 pos[i] = i; 79 scanf("%d", &w[i]); 80 } 81 82 memset(h, -1, sizeof(h)); 83 for (int i = 1; i <= m; i++) 84 { 85 int a, b; 86 int c = n + i; 87 scanf("%d%d", &a, &b); 88 add(c, pos[a]); 89 add(c, pos[b]); 90 pos[b] = c; 91 } 92 93 for (int i = n + m; i; i--) 94 { 95 if (!F[i][0]) dfs(i); 96 } 97 98 for (int i = 1; i <= q; i++) 99 { 100 int a, b; 101 scanf("%d%d", &a,&b); 102 int lca = LCA(a, b); 103 if (!lca) continue; 104 rea[i] = Node(a, b, dep[lca], i); 105 } 106 107 sort(rea + 1, rea + 1 + q, cmp); 108 109 long long ans = 0; 110 for (int i = 1; i <= q; i++) 111 { 112 int a = rea[i].a, b = rea[i].b; 113 int t = min(w[a], w[b]); 114 ans += t * 2; 115 w[a] -= t, w[b] -= t; 116 } 117 118 printf("%lld", ans); 119 }
树分治:
核心:
用来处理树上所有点对路径的问题,每次递归找出当前子树的重心,经过重心的路径进行统计,该重心统计完后再递归子树进行相同操作。
【CF293E】Close Vertices(题目):
每次递归找出子树的重心,从重心往其他点查询满足的点对数。
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int N = 2000010; 9 10 int n, l, w; 11 long long ans; 12 int t[N], f[N]; 13 int head, tail, sum, root; 14 int size1[N], dep[N], len[N], dis[N]; 15 int h[N], num[N], nex[N], v[N], dqx; 16 bool st[N]; 17 18 queue<int> que; 19 20 struct Node 21 { 22 int l, d; 23 Node() {} 24 Node(int l, int d) :l(l), d(d) {} 25 }; 26 27 Node q[N]; 28 29 bool cmp1(Node x, Node y) 30 { 31 return x.d < y.d; 32 } 33 34 bool cmp2(Node x, Node y) 35 { 36 return x.d > y.d; 37 } 38 39 void add(int a, int b, int c) 40 { 41 num[dqx] = b; 42 v[dqx] = c; 43 nex[dqx] = h[a]; 44 h[a] = dqx++; 45 } 46 47 void get_root(int u, int fa) 48 { 49 size1[u] = 1; 50 f[u] = 0; 51 for (int i = h[u]; ~i; i = nex[i]) 52 { 53 int j = num[i]; 54 if ((j == fa) || st[j]) continue; 55 56 get_root(j, u); 57 size1[u] += size1[j]; 58 f[u] = max(f[u], size1[j]); 59 } 60 61 f[u] = max(f[u], sum - size1[u]); 62 if ((f[u] < f[root]) || (!root)) root = u; 63 } 64 65 int lowbit(int x) 66 { 67 return x & -x; 68 } 69 70 void update(int u, int k) 71 { 72 while (u <= n) 73 { 74 t[u] += k; 75 u += lowbit(u); 76 } 77 } 78 79 int get(int x) 80 { 81 int res = 0; 82 while (x > 0) 83 { 84 res += t[x]; 85 x -= lowbit(x); 86 } 87 return res; 88 } 89 90 void dfs(int u, int fa) 91 { 92 size1[u] = 1; 93 for (int i = h[u]; ~i; i = nex[i]) 94 { 95 int j = num[i]; 96 if (j == fa || st[j]) continue; 97 98 len[j] = len[u] + 1; 99 dis[j] = dis[u] + v[i]; 100 q[++tail] = Node(len[j], dis[j]); 101 102 dfs(j, u); 103 104 size1[u] += size1[j]; 105 if (u == root) 106 { 107 sort(q + 1, q + head + 1, cmp1); 108 sort(q + head + 1, q + tail + 1, cmp2); 109 int k = 1; 110 for (int j = head + 1; j <= tail; j++) 111 { 112 while ((q[k].d + q[j].d <= w) && (k <= head)) 113 { 114 update(q[k].l + 1, 1); 115 k++; 116 } 117 ans += (long long)get(l - q[j].l + 1); 118 } 119 for (int g = 1; g < k; g++) update(q[g].l + 1, -1); 120 head = tail; 121 que.push(j); 122 } 123 } 124 } 125 126 int main() 127 { 128 scanf("%d%d%d", &n, &l, &w); 129 130 memset(h, -1, sizeof(h)); 131 for (int i = 2; i <= n; i++) 132 { 133 int x, d; 134 scanf("%d%d", &x, &d); 135 add(i, x, d), add(x, i, d); 136 } 137 138 que.push(1); 139 size1[1] = n; 140 while (!que.empty()) 141 { 142 int u = que.front(); 143 que.pop(); 144 145 sum = size1[u]; 146 root = 0; 147 get_root(u, 0); 148 len[root] = dis[root] = 0; 149 150 head = 1, tail = 1; 151 q[1] = Node(0, 0); 152 dfs(root, 0); 153 st[root] = true; 154 } 155 printf("%lld", ans); 156 }
【NOI2014】购票(题目):
设f[u]为u到1的最小代价,则:
设d[u]=dis[u]×pu+qu,则:
若有两个合法的转移点j,k,比较他们转移的优劣:
当j比k更优时,则:
即点(dis[j],f[j])和点(dis[k],f[k])之间的斜率大于pu,用所以我们需要用单调栈维护一个合法点的下凸壳,这样可以根据不同的 pu 在凸壳上二分得到决策点。维护一些区间中的凸壳,查询时选择一些区间的并恰好等于需要的后缀,在每个区间内分别查询最优决策并更新。用树状数组维护后缀信息
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 typedef long long LL; 9 10 const int N = 4000010; 11 const LL INF = 2e18 + 7; 12 13 int n, faz[N]; 14 LL wgh[N]; 15 LL dep[N], dis[N]; 16 LL p[N], q[N], b[N], len[N]; 17 LL stds[N], tds; 18 LL f[N], * X = dis, * Y = f; 19 20 std::vector<int> G[N]; 21 22 int stkp[N], valp[N], tpp[N]; 23 int* istk[N], * ival[N], * itp[N], it[N]; 24 25 double slope(int i, int j) 26 { 27 return X[i] == X[j] ? 1e60 : (double)(Y[j] - Y[i]) / (X[j] - X[i]); 28 } 29 30 void push(int id, int u) 31 { 32 int t = it[id], tp = itp[id][t], * stk = istk[id]; 33 while (tp > 0 && slope(stk[tp - 1], stk[tp]) >= slope(stk[tp], u)) --tp; 34 ++t, ival[id][t] = stk[itp[id][t] = ++tp], stk[tp] = u, it[id] = t; 35 } 36 37 void pop(int id) 38 { 39 istk[id][itp[id][it[id]]] = ival[id][it[id]]; 40 --it[id]; 41 } 42 43 int check(int id, LL slp) 44 { 45 int t = it[id], tp = itp[id][t], * stk = istk[id]; 46 47 if (!~tp) return -1; 48 int lb = 0, rb = tp - 1, x = tp, mid; 49 50 while (lb <= rb) 51 { 52 mid = (lb + rb) >> 1; 53 if (slope(stk[mid], stk[mid + 1]) <= slp) lb = mid + 1; 54 else x = mid, rb = mid - 1; 55 } 56 return stk[x]; 57 } 58 59 void push_all(int i, int u) 60 { 61 for (; i <= n; i += i & -i) push(i, u); 62 } 63 64 void pop_all(int i) 65 { 66 for (; i <= n; i += i & -i) pop(i); 67 } 68 69 void get_DP(int u) 70 { 71 f[u] = INF; 72 int i = lower_bound(stds + 1, stds + tds + 1, dis[u] - len[u]) - stds; 73 for (i = n - i + 1; i; i -= i & -i) 74 { 75 int v = check(i, p[u]); 76 if (~v) f[u] = std::min(f[u], f[v] - dis[v] * p[u] + b[u]); 77 } 78 } 79 80 void dfs(int u) 81 { 82 dep[u] = dep[faz[u]] + 1; 83 dis[u] = dis[faz[u]] + wgh[u]; 84 stds[++tds] = dis[u]; 85 b[u] = dis[u] * p[u] + q[u]; 86 87 get_DP(u); 88 push_all(n - dep[u], u); 89 for (auto v : G[u]) dfs(v); 90 pop_all(n - dep[u]); 91 --tds; 92 } 93 94 int main() 95 { 96 scanf("%d%*d", &n); 97 98 istk[1] = stkp; 99 itp[1] = tpp; 100 ival[1] = valp; 101 itp[1][it[1] = 0] = -1; 102 103 for (int i = 2; i <= n; ++i) 104 { 105 int lenl = ((i - 1) & (1 - i)) + 1; 106 istk[i] = istk[i - 1] + lenl; 107 itp[i] = itp[i - 1] + lenl; 108 ival[i] = ival[i - 1] + lenl; 109 itp[i][it[i] = 0] = -1; 110 } 111 112 for (int i = 2; i <= n; ++i) 113 { 114 scanf("%d%lld%lld%lld%lld", &faz[i], &wgh[i], &p[i], &q[i], &len[i]); 115 G[faz[i]].push_back(i); 116 } 117 118 push_all(n, 1); 119 stds[tds = 1] = 0; 120 121 for (auto u : G[1]) dfs(u); 122 for (int i = 2; i <= n; ++i) printf("%lld\n", f[i]); 123 }