• 家庭主妇问题


    一 问题描述

    X 村的人们住在美丽的小屋里。若两个小屋通过双向道路连接,则可以说这两个小屋直接相连。X 村非常特别,可以从任意小屋到达任意其他小屋,每两个小屋之间的路线都是唯一的。温迪的孩子喜欢去找其他孩子玩,然后打电话给温迪:“妈咪,带我回家!”。在不同的时间沿道路行走所需的时间可能不同。温迪想告诉她的孩子她将在路上花的确切时间。

    二 输入和输出说明

    1 输入

    第 1 行包含 3 个整数 n 、q 、s,表示有 n 个小屋、q 个消息,温迪目前在 s 小屋里,n <100001,q <100001。以下 n -1 行各包含 3 个整数 a、b 和 w ,表示有一条连接小屋 a 和 b 的道路,所需的时间是 w (1≤w≤10000)。以下 q 行有两种消息类型:

    ① 消息 A,即 0 u ,孩子在小屋 u 中给温迪打电话,温迪应该从现在的位置去小屋 u ;

    ② 消息 B,即 1 i w ,将第 i 条道路所需的时间修改为 w 

    注意:温迪在途中时,时间不会发生改变,时间在温迪停留在某个地方等待孩子时才会改变)。

    2 输出

    对每条消息 A,都输出一个整数,即找到孩子所需的时间。

    三 输入和输出用例

    1 输入样例

    3 3 1

    1 2 1

    2 3 2

    0 2

    1 2 3

    0 3

    2 输出样例

    1

    3

    四 分析

    本问题中任意两个小屋都可以相互到达,且路径唯一,明显是树形结构。可以将边权看作点权,对一条边,让深度 dep 较大的点存储边权。对边 u 、v ,边权为 w ,若 dep[u ]>dep[v ],则视 u 的权值为 w 。本问题包括树上点更新、区间和查询。可以用树链剖分将树形结构线性化,然后用线段树进行点更新、区间和查询。

    解决方案:树链剖分+线段树。

    五 算法设计

    1 第 1 次深度优先遍历求 dep、fa、size、son,第 2 次深度优先遍历求 top、id、rev。

    2 创建线段树

    3 点更新,u 对应的下标 i =id[u],将其值更新为 val。

    4 区间查询,求 u 、v 之间的和值。若 u 、v 不在同一条重链上,则一边查询,一边向同一条重链靠拢;若 u 、v 在同一条重链上,则根据节点的下标在线段树中进行区间查询。

    注意:因为在本题中是将边权转变为点权,所以实际查询的区间应为 query(1, id[son[u]], id[v])。

    六 代码

    1. package com.platform.modules.alg.alglib.poj2763;
    2. public class Poj2763 {
    3. private int maxn = 100010;
    4. int head[] = new int[maxn]; //头结点
    5. int cnt = 0;
    6. int total = 0;
    7. int fa[] = new int[maxn]; // 父亲
    8. int dep[] = new int[maxn]; // 深度
    9. int size[] = new int[maxn]; // 子树结点总数
    10. int son[] = new int[maxn]; // 重儿子
    11. int top[] = new int[maxn]; // 所在重链顶端结点
    12. int id[] = new int[maxn];
    13. int rev[] = new int[maxn]; // u 对应的 dfs 序下标,下标对于的 u
    14. int Sum;
    15. Edge1 a[] = new Edge1[maxn];
    16. edge e[] = new edge[maxn << 1];
    17. node tree[] = new node[maxn << 2];
    18. public Poj2763() {
    19. for (int i = 0; i < a.length; i++) {
    20. a[i] = new Edge1();
    21. }
    22. for (int i = 0; i < e.length; i++) {
    23. e[i] = new edge();
    24. }
    25. for (int i = 0; i < tree.length; i++) {
    26. tree[i] = new node();
    27. }
    28. }
    29. public String output = "";
    30. public String cal(String input) {
    31. int n, q, s;
    32. init();
    33. String[] line = input.split("\n");
    34. String[] words = line[0].split(" ");
    35. n = Integer.parseInt(words[0]);
    36. q = Integer.parseInt(words[1]);
    37. s = Integer.parseInt(words[2]);
    38. for (int i = 1; i < n; i++) {
    39. String[] info = line[i].split(" ");
    40. a[i].u = Integer.parseInt(info[0]);
    41. a[i].v = Integer.parseInt(info[1]);
    42. a[i].w = Integer.parseInt(info[2]);
    43. add(a[i].u, a[i].v);
    44. add(a[i].v, a[i].u);
    45. }
    46. dep[1] = 1;
    47. dfs1(1, 0);
    48. dfs2(1, 1);
    49. build(1, 1, total);//创建线段树
    50. for (int i = 1; i < n; i++) {
    51. if (dep[a[i].u] > dep[a[i].v]) {
    52. int temp = a[i].u;
    53. a[i].u = a[i].v;
    54. a[i].v = temp;
    55. }
    56. update(1, id[a[i].v], a[i].w);
    57. }
    58. int opt, i, val, x;
    59. while (q-- > 0) {
    60. String[] query = line[n++].split(" ");
    61. opt = Integer.parseInt(query[0]);
    62. if (opt == 1) {
    63. i = Integer.parseInt(query[1]);
    64. val = Integer.parseInt(query[2]);
    65. update(1, id[a[i].v], val); // 改变第 i 条边的值为 val
    66. } else {
    67. x = Integer.parseInt(query[1]);
    68. Sum = 0;
    69. ask(s, x);//查询s->x路径上边权的和值
    70. output += Sum + "\n";
    71. s = x;//更新温迪的位置
    72. }
    73. }
    74. return output;
    75. }
    76. void add(int u, int v) {
    77. e[++cnt].to = v;
    78. e[cnt].next = head[u];
    79. head[u] = cnt;
    80. }
    81. void init() {
    82. cnt = total = 0;
    83. }
    84. // 求dep,fa,size,son
    85. void dfs1(int u, int f) {
    86. size[u] = 1;
    87. for (int i = head[u]; i > 0; i = e[i].next) {
    88. int v = e[i].to;
    89. if (v == f)//父节点
    90. continue;
    91. dep[v] = dep[u] + 1;//深度
    92. fa[v] = u;
    93. dfs1(v, u);
    94. size[u] += size[v];
    95. if (size[v] > size[son[u]])
    96. son[u] = v;
    97. }
    98. }
    99. // 求 top,id,rev
    100. void dfs2(int u, int t) {
    101. top[u] = t;
    102. id[u] = ++total;//u对应的dfs序下标
    103. rev[total] = u;//dfs序下标对应的结点u
    104. if (son[u] == 0)
    105. return;
    106. dfs2(son[u], t);//沿着重儿子dfs
    107. for (int i = head[u]; i > 0; i = e[i].next) {
    108. int v = e[i].to;
    109. if (v != fa[u] && v != son[u])
    110. dfs2(v, v);
    111. }
    112. }
    113. // 点更新,线段树的第 k 个值为 val
    114. void update(int i, int k, int val) {
    115. if (tree[i].l == k && tree[i].r == k) {
    116. tree[i].sum = val;
    117. return;
    118. }
    119. int mid = (tree[i].l + tree[i].r) / 2;
    120. if (k <= mid) update(i << 1, k, val);
    121. else update((i << 1) | 1, k, val);
    122. tree[i].sum = tree[i << 1].sum + tree[(i << 1) | 1].sum;
    123. }
    124. // 初始化线段树,i 表示存储下标,区间[l,r]
    125. void build(int i, int l, int r) {
    126. tree[i].l = l;
    127. tree[i].r = r;
    128. tree[i].sum = 0;
    129. if (l == r) return;
    130. int mid = (l + r) / 2;//划分点
    131. build(i << 1, l, mid);
    132. build((i << 1) | 1, mid + 1, r);
    133. }
    134. // 查询线段树中 [l,r] 的和值
    135. void query(int i, int l, int r) {
    136. if (tree[i].l >= l && tree[i].r <= r) {//找到该区间
    137. Sum += tree[i].sum;
    138. return;
    139. }
    140. int mid = (tree[i].l + tree[i].r) / 2;
    141. if (l <= mid) query(i << 1, l, r);
    142. if (r > mid) query((i << 1) | 1, l, r);
    143. }
    144. void ask(int u, int v) {//求u,v之间的和值
    145. while (top[u] != top[v]) {//不在同一条重链上
    146. if (dep[top[u]] < dep[top[v]]) {
    147. int temp = u;
    148. u = v;
    149. v = temp;
    150. }
    151. query(1, id[top[u]], id[u]);//u顶端结点和u之间
    152. u = fa[top[u]];
    153. }
    154. if (u == v) return;
    155. if (dep[u] > dep[v]) {//在同一条重链上
    156. int temp = u; //深度小的结点为u
    157. u = v;
    158. v = temp;
    159. }
    160. query(1, id[son[u]], id[v]);//注意是son[u]
    161. }
    162. }
    163. class edge {
    164. int to, next;
    165. }
    166. // 结点
    167. class node {
    168. int l, r, sum; // l,r区间左右端点,区间和值
    169. }
    170. class Edge1 {
    171. int u, v, w;
    172. }

    七 测试

  • 相关阅读:
    QT软件开发-基于FFMPEG设计录屏与rtsp、rtmp推流软件(支持桌面与摄像头)(二)
    WordPress主题开发(五)之—— 主题结构基础补存
    【idea插件开发】从0入门idea插件开发,idea插件开发教程,如何开发idea插件
    使用 OpenCV 进行图像操作:腐蚀、膨胀等等
    GO中的文件操作
    记-数据库事务隔离级别
    【云原生之Docker实战】使用docker部署Wiznote私人笔记系统
    利用SD存储介质扩展MAXQ20000的非易失性数据存储空间
    无人机避障技术
    kubectl系列(三)-节点标签操作
  • 原文地址:https://blog.csdn.net/chengqiuming/article/details/127739645