题目链接

【洛谷】
【BZOJ】

题目描述

Ray 乐忠于旅游,这次他来到了T 城。T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接。为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间有且只有一条路径。换句话说, T 城中只有N − 1 座桥。
Ray 发现,有些桥上可以看到美丽的景色,让人心情愉悦,但有些桥狭窄泥泞,令人烦躁。于是,他给每座桥定义一个愉悦度w,也就是说,Ray 经过这座桥会增加w 的愉悦度,这或许是正的也可能是负的。有时,Ray 看待同一座桥的心情也会发生改变。
现在,Ray 想让你帮他计算从u 景点到v 景点能获得的总愉悦度。有时,他还想知道某段路上最美丽的桥所提供的最大愉悦度,或是某段路上最糟糕的一座桥提供的最低愉悦度。

输入的第一行包含一个整数N,表示T 城中的景点个数。景点编号为 0...N − 1。
接下来N − 1 行,每行三个整数u、v 和w,表示有一条u 到v,使 Ray 愉悦度增加w 的桥。桥的编号为1...N − 1。|w| <= 1000。 输入的第N + 1 行包含一个整数M,表示Ray 的操作数目。
接下来有M 行,每行描述了一个操作,操作有如下五种形式:

  • C i w,表示Ray 对于经过第i 座桥的愉悦度变成了w。
  • N u v,表示Ray 对于经过景点u 到v 的路径上的每一座桥的愉悦度都变成原来的相反数。
  • SUM u v,表示询问从景点u 到v 所获得的总愉悦度。
  • MAX u v,表示询问从景点u 到v 的路径上的所有桥中某一座桥所提供的最大愉悦度。
  • MIN u v,表示询问从景点u 到v 的路径上的所有桥中某一座桥所提供的最小愉悦度。
    测试数据保证,任意时刻,Ray 对于经过每一座桥的愉悦度的绝对值小于等于1000。

题解

思路比较简单的树链剖分,但是码量实现起来比较烦,足足写了180+行。
一个一个操作分析过来:

操作1 修改边权

我们都知道,树链剖分可以修改点权,那么同理也可以修改边权。
观察一棵树会发现,一条边有且仅有一个独立的儿子,那么我们就可以把这个信息放到这个儿子上,就变成了维护点权的问题了。
但是需要注意,两个点的lca上的信息并不是两个点之间的路径的信息。
回忆一下我们树链剖分求lca的过程,会发现我们是最后查找的lca。那么同理,如果两个点在一个重链上,那么深度较小的点就是lca,那么我们就操作这个节点的编号\(+1\)的点到深度较大的点。
具体实现差不多是这个样的一个模板:

  1. void Orz(int u, int v) {
  2. while (top[u] != top[v]) {
  3. if (dep[top[u]] < dep[top[v]]) swap(u, v);
  4. segment_tree_Orz(1, idx[top[u]], idx[u]);
  5. u = fa[top[u]];
  6. }
  7. if (dep[u] > dep[v]) swap(u, v);
  8. segment_tree_Orz(1, idx[u] + 1, idx[v]);
  9. }

操作2 路径信息取反

很容易可以得到,如果我们用线段树维护区间最大值和最小值、和。
那么取反后,最大值的相反数变成了最小值,最小值的相反数变成了最大值,然后和就只需要取反就可以了一开始WA了好几发就是因为取反的时候顺序写错了(大雾

操作3&4&5 区间最大最小和

这些操作都是线段树的基本操作。不做赘述。

复杂度分析

时间复杂度:\(O(nlogm)\)
空间复杂度:\(O(4\times n)\)
代码复杂度:较复杂,细节比较多。
思路难度:简单。

代码

  1. #include <bits/stdc++.h>
  2. #define ms(a, b) memset(a, b, sizeof(a))
  3. #define ll long long
  4. #define ull unsigned long long
  5. #define ms(a, b) memset(a, b, sizeof(a))
  6. #define inf 0x3f3f3f3f
  7. #define db double
  8. #define Pi acos(-1)
  9. #define eps 1e-8
  10. #define N 100005
  11. using namespace std;
  12. template <typename T> void read(T &x) {
  13. x = 0; T fl = 1; char ch = 0;
  14. for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') fl = -1;
  15. for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
  16. x *= fl;
  17. }
  18. template <typename T> void write(T x) {
  19. if (x < 0) x = -x, putchar('-');
  20. if (x > 9) write(x / 10); putchar(x % 10 + '0');
  21. }
  22. template <typename T> void writeln(T x) { write(x); puts(""); }
  23. struct Segment_Tree {
  24. #define lc (nod << 1)
  25. #define rc (nod << 1 | 1)
  26. struct node {
  27. int mx, mi, l, r, tag, s;
  28. }tr[N << 2];
  29. void pushup(int nod) { tr[nod].mx = max(tr[lc].mx, tr[rc].mx); tr[nod].s = tr[lc].s + tr[rc].s; tr[nod].mi = min(tr[lc].mi, tr[rc].mi); }
  30. void pushdown(int nod) {
  31. int tmp;
  32. if (tr[nod].tag) {
  33. tr[lc].tag ^= 1; tr[rc].tag ^= 1;
  34. tmp = tr[lc].mi; tr[lc].mi = -tr[lc].mx; tr[lc].mx = -tmp;
  35. tmp = tr[rc].mi; tr[rc].mi = -tr[rc].mx; tr[rc].mx = -tmp;
  36. tr[lc].s *= -1; tr[rc].s *= -1;
  37. tr[nod].tag ^= 1;
  38. }
  39. }
  40. void build(int nod, int l, int r, int *a) {
  41. tr[nod].l = l, tr[nod].r = r; tr[nod].mi = inf, tr[nod].mx = -inf, tr[nod].s = 0; tr[nod].tag = 0;
  42. if (l == r) { tr[nod].mx = tr[nod].mi = tr[nod].s = a[l]; return ; }
  43. int mid = (l + r) >> 1;
  44. build(lc, l, mid, a); build(rc, mid + 1, r, a);
  45. pushup(nod);
  46. }
  47. void update1(int nod, int k, int val) { // 单点修改
  48. int l = tr[nod].l, r = tr[nod].r;
  49. if (l == r) { tr[nod].s = tr[nod].mx = tr[nod].mi = val; return; }
  50. pushdown(nod);
  51. int mid = (l + r) >> 1;
  52. if (k <= mid) update1(lc, k, val); else update1(rc, k, val);
  53. pushup(nod);
  54. }
  55. void update2(int nod, int ql, int qr) { // 相反数
  56. int l = tr[nod].l, r = tr[nod].r;
  57. pushdown(nod);
  58. if (ql <= l && r <= qr) {int tmp = tr[nod].mi; tr[nod].mi = -tr[nod].mx; tr[nod].mx = -tmp; tr[nod].s *= -1; tr[nod].tag ^= 1; return; }
  59. int mid = (l + r) >> 1;
  60. if (ql <= mid) update2(lc, ql, qr);
  61. if (qr > mid) update2(rc, ql, qr);
  62. pushup(nod);
  63. }
  64. int query_sec_min(int nod, int ql, int qr) { // 取最小值
  65. int l = tr[nod].l, r = tr[nod].r;
  66. pushdown(nod);
  67. if (ql <= l && r <= qr) return tr[nod].mi;
  68. int mid = (l + r) >> 1, res = inf;
  69. if (ql <= mid) res = min(res, query_sec_min(lc, ql, qr));
  70. if (qr > mid) res = min(res, query_sec_min(rc, ql, qr));
  71. return res;
  72. }
  73. int query_sec_max(int nod, int ql, int qr) { // 取最大值
  74. int l = tr[nod].l, r = tr[nod].r;
  75. pushdown(nod);
  76. if (ql <= l && r <= qr) return tr[nod].mx;
  77. int mid = (l + r) >> 1, res = -inf;
  78. if (ql <= mid) res = max(res, query_sec_max(lc, ql, qr));
  79. if (qr > mid) res = max(res, query_sec_max(rc, ql, qr));
  80. return res;
  81. }
  82. int query_sec_sum(int nod, int ql, int qr) { // 区间求和
  83. int l = tr[nod].l, r = tr[nod].r;
  84. pushdown(nod);
  85. if (ql <= l && r <= qr) return tr[nod].s;
  86. int mid = (l + r) >> 1, res = 0;
  87. if (ql <= mid) res += query_sec_sum(lc, ql, qr);
  88. if (qr > mid) res += query_sec_sum(rc, ql, qr);
  89. return res;
  90. }
  91. }sgt;
  92. struct edge {
  93. int to, nt, w;
  94. }E[N << 1];
  95. char opt[5];
  96. int fa[N], sz[N], son[N], dep[N], top[N], H[N], a[N], val[N], idx[N], U[N], V[N];
  97. int tot, cnt, n, m;
  98. void add_edge(int u, int v, int w) {
  99. E[++ cnt] = (edge){v, H[u], w};
  100. H[u] = cnt;
  101. }
  102. void dfs1(int u, int ft, int dp) {
  103. fa[u] = ft; dep[u] = dp; sz[u] = 1;
  104. int maxson = -1;
  105. for (int e = H[u]; e; e = E[e].nt) {
  106. int v = E[e].to; if (v == ft) continue;
  107. dfs1(v, u, dp + 1);
  108. a[v] = E[e].w; sz[u] += sz[v];
  109. if (maxson < sz[v]) son[u] = v, maxson = sz[v];
  110. }
  111. }
  112. void dfs2(int u, int tp) {
  113. top[u] = tp; idx[u] = ++ tot; val[tot] = a[u];
  114. if (!son[u]) return; dfs2(son[u], tp);
  115. for (int e = H[u]; e; e = E[e].nt) {
  116. int v = E[e].to; if (v == fa[u] || v == son[u]) continue;
  117. dfs2(v, v);
  118. }
  119. }
  120. void update_chain_1(int x, int val) {
  121. int u = U[x], v = V[x];
  122. if (u == fa[v]) swap(u, v);
  123. sgt.update1(1, idx[u], val);
  124. }
  125. void update_chain_2(int u, int v) {
  126. while (top[u] != top[v]) {
  127. if (dep[top[u]] < dep[top[v]]) swap(u, v);
  128. sgt.update2(1, idx[top[u]], idx[u]);
  129. u = fa[top[u]];
  130. }
  131. if (dep[u] > dep[v]) swap(u, v);
  132. sgt.update2(1, idx[u] + 1, idx[v]);
  133. }
  134. int query_chain_sum(int u, int v) {
  135. int res = 0;
  136. while (top[u] != top[v]) {
  137. if (dep[top[u]] < dep[top[v]]) swap(u, v);
  138. res += sgt.query_sec_sum(1, idx[top[u]], idx[u]);
  139. u = fa[top[u]];
  140. }
  141. if (dep[u] > dep[v]) swap(u, v);
  142. res += sgt.query_sec_sum(1, idx[u] + 1, idx[v]);
  143. return res;
  144. }
  145. int query_chain_min(int u, int v) {
  146. int res = inf;
  147. while (top[u] != top[v]) {
  148. if (dep[top[u]] < dep[top[v]]) swap(u, v);
  149. res = min(res, sgt.query_sec_min(1, idx[top[u]], idx[u]));
  150. u = fa[top[u]];
  151. }
  152. if (dep[u] > dep[v]) swap(u, v);
  153. res = min(res, sgt.query_sec_min(1, idx[u] + 1, idx[v]));
  154. return res;
  155. }
  156. int query_chain_max(int u, int v) {
  157. int res = -inf;
  158. while (top[u] != top[v]) {
  159. if (dep[top[u]] < dep[top[v]]) swap(u, v);
  160. res = max(res, sgt.query_sec_max(1, idx[top[u]], idx[u]));
  161. u = fa[top[u]];
  162. }
  163. if (dep[u] > dep[v]) swap(u, v);
  164. res = max(res, sgt.query_sec_max(1, idx[u] + 1, idx[v]));
  165. return res;
  166. }
  167. int main() {
  168. read(n);
  169. for (int i = 1; i < n; i ++) {
  170. int u, v, w; read(u); read(v); read(w); u ++, v ++;
  171. add_edge(u, v, w); add_edge(v, u, w);
  172. U[i] = u; V[i] = v;
  173. }
  174. dfs1(1, 0, 1); dfs2(1, 1);
  175. sgt.build(1, 1, n, val);
  176. read(m);
  177. while (m --) {
  178. scanf("%s", opt);
  179. if (opt[0] == 'C') { int i, w; read(i); read(w); update_chain_1(i, w); }
  180. if (opt[0] == 'N') { int u, v; read(u); read(v); ++ u, ++ v; update_chain_2(u, v); }
  181. if (opt[0] == 'S') { int u, v; read(u); read(v); ++ u, ++ v; writeln(query_chain_sum(u, v)); }
  182. if (opt[1] == 'I') { int u, v; read(u); read(v); ++ u, ++ v; writeln(query_chain_min(u, v)); }
  183. if (opt[1] == 'A') { int u, v; read(u); read(v); ++ u, ++ v; writeln(query_chain_max(u, v)); }
  184. }
  185. return 0;
  186. }

⌈洛谷1505⌋⌈BZOJ2157⌋⌈国家集训队⌋旅游【树链剖分】的更多相关文章

  1. 洛谷 P1505 [国家集训队]旅游 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例: 输出样例: 说明 思路 AC代码 总结 题面 题目链接 P1505 [国家集训队]旅游 题目描述 Ray 乐 ...

  2. LG1505 [国家集训队]旅游 树链剖分

    问题描述 LG1505 题解 边化点权. 超级多操作的树剖板子... 以后就拿这个当树剖板子复习吧... \(\mathrm{Code}\) #include<bits/stdc++.h> ...

  3. LUOGU P1505 [国家集训队]旅游 (树链剖分+线段树)

    传送门 解题思路 快被调死的码农题,,,其实就是一个边权下放到点权的线段树+树剖. #include<iostream> #include<cstdio> #include&l ...

  4. 树链剖分【洛谷P1505】 [国家集训队]旅游

    P1505 [国家集训队]旅游 题目描述 Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T 城 ...

  5. 洛谷P3178 [HAOI2015]树上操作 题解 树链剖分+线段树

    题目链接:https://www.luogu.org/problem/P3178 这道题目是一道树链剖分的模板题. 但是在解决这道问题的同事刷新了我的两个认识: 第一个认识是:树链剖分不光可以处理链, ...

  6. 【洛谷4719】 动态dp(树链剖分,dp,矩阵乘法)

    前言 其实我只是为了过掉模板而写的ddp,实际应用被吊着锤 Solution 并不想写详细的过程 一句话过程:将子树中轻儿子的贡献挂到这个点上面来 详细版:(引用yyb) 总结一下的话,大致的过程是这 ...

  7. 【洛谷 P4211】[LNOI2014]LCA(树链剖分,差分)

    题目链接 看到题目肯定首先想到要求LCA(其实是我菜),可乍一看,n与q的规模为5W, 求LCA的复杂度为\(O(logN)\),那么总时间复杂度为\(O(nq\ log\ n)\). 怎么搞呢? 会 ...

  8. 洛谷 P2146 [NOI2015]软件包管理器 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例#1: 输出样例#1: 输入样例#2: 输出样例#2: 说明 说明 思路 AC代码 总结 题面 题目链接 P ...

  9. BZOJ2157旅游——树链剖分+线段树

    题目描述 Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T 城的任意两个景点之间有且只有一条路 ...

随机推荐

  1. JS_左边栏菜单

    需求: 要求实现左边栏菜单点击一下就弹开,其他的隐藏.再点击一下就隐藏. 最多只能有一个菜单的详细内容会显示出来. 三个菜单实现联动效果. 代码如下: 1 <!DOCTYPE html> ...

  2. mybatis源码分析(三)------------映射文件的解析

    本篇文章主要讲解映射文件的解析过程 Mapper映射文件有哪几种配置方式呢?看下面的代码: <!-- 映射文件 --> <mappers> <!-- 通过resource ...

  3. 解决ERROR 1130: Host '192.168.11.1' is not allowed to connect to this MySQL

    使用navicat进行远程登录MySQL时,报出 ERROR 1130: Host '192.168.11.1' is not allowed to connect to this MySQL  se ...

  4. Linux基础学习笔记5-软件管理

    包管理器 二进制应用程序的组成部分: 二进制文件.库文件.配置文件.帮助文件 程序包管理器: debian:deb文件.dpkg包管理器 redhat:rpm文件.rpm包管理器 rpm:Redhat ...

  5. 二进制安装MongoDB

    1.下载mongodb cd /usr/local/src/ wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.5.tgz ...

  6. linux 查看TCP端口

    如有转载,不胜荣幸.http://www.cnblogs.com/aaron-agu/ netstat –nat

  7. 开发中遇到的css兼容问题

    1. overflow: scroll(平台兼容) 在Mac中的Chrome浏览器中,内容不超过容器时不会出现滚动条: 在Wins中的Chorme浏览器中,内容不超过容器时也会出现滚动条. 解决方法: ...

  8. CSS 背景图片 添加 重复和定位。

    <!doctype html><html lang="en"> <head> <meta charset="UTF-8" ...

  9. CSS 常见的8种选择器 和 文本溢出问题

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>c ...

  10. jQuery AJAX获取JSON数据解析多种方式示例

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...