一、内容

题目背景

深绘里一直很讨厌雨天。灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。无奈的深绘里和村民们只好等待救济粮来维生。不过救济粮的发放方式很特别。

题目描述

首先村落里的一共有 nnn 座房屋,并形成一个树状结构。然后救济粮分 mmm 次发放,每次选择两个房屋 (x, y)(x,~y)(x, y),然后对于 xxx 到 yyy 的路径上(含 xxx 和 yyy)每座房子里发放一袋 zzz 类型的救济粮。然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。
输入格式输入的第一行是两个用空格隔开的正整数,分别代表房屋的个数 nnn 和救济粮发放的次数 mmm。第 222 到 第 nnn 行,每行有两个用空格隔开的整数 a, ba,~ba, b,代表存在一条连接房屋 aaa 和 bbb 的边。第 (n+1)(n + 1)(n+1) 到第 (n+m)(n + m)(n+m) 行,每行有三个用空格隔开的整数 x, y, zx,~y,~zx, y, z,代表一次救济粮的发放是从 xxx 到 yyy 路径上的每栋房子发放了一袋 zzz 类型的救济粮。
输出格式输出 nnn 行,每行一个整数,第 iii 行的整数代表 iii 号房屋存放最多的救济粮的种类,如果有多种救济粮都是存放最多的,输出种类编号最小的一种。如果某座房屋没有救济粮,则输出 000。

输入 #1

5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3

输出 #1

2
3
3
0
2

二、思路

  • 前缀知识 : 动态开点 +线段树合并

  • 首先我们每个点都有m类救济粮,统计最大的救济粮个数的种类。 我们可以每个点都创建一个线段树。里面保存1~m类救济粮的最大值。

  • 当对u—>v 的路径进行添加救济粮时,我们可以利用树上差分

    • 每次对u的线段树的z类进行加1, 对v的线段树的z类救济粮个数加1, 对lca的z类救济粮个数-1, 对lca的父节点救济粮个数再-1。 这样对u–>v的每个救济粮个数进行差分。
  • 最后dfs的时候,只需要将2个点的线段树进行合并 。 这样里面的每类的最大就合并到一个点里面去了。

三、代码

#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
struct Node {int lc, rc, mx, id; //mx保存最大值 id保存其编号
} tr[N * 80];
struct E {int v, next;} e[N << 1];
int n, m, u[N], v[N], z[N], idz[N], cntz, lg, len, cnt, h[N], f[N][18], dep[N], rt[N], ans[N]; //cnt是动态开点的个数 rt代表每个节点的线段树根节点编号
void add(int u, int v) {e[++len].v = v; e[len].next = h[u]; h[u] = len;}
void bfs() {dep[1] = 1; queue<int> q; q.push(1); while (!q.empty()) {int u = q.front(); q.pop();for (int j = h[u]; j; j = e[j].next) {int v = e[j].v;if (dep[v]) continue;dep[v] = dep[u] + 1;q.push(v); f[v][0] = u;for (int k = 1; k <= lg; k++) f[v][k] = f[f[v][k - 1]][k - 1];}}
}
int LCA(int x, int y) {if (dep[y] > dep[x]) swap(x, y);for (int k = lg; k >= 0; k--) {if (dep[f[x][k]] >= dep[y]) x = f[x][k];}if (x == y) return x;for (int k = lg; k >= 0; k--) {if (f[x][k] != f[y][k]) x = f[x][k], y = f[y][k];}return f[x][0];
}
void pushup(int id) {int lc = tr[id].lc, rc = tr[id].rc;//左边区间的编号肯定是小于右边的  所以当救济相等时存储左边的编号 if (tr[lc].mx >= tr[rc].mx) tr[id].mx = tr[lc].mx, tr[id].id = tr[lc].id;else tr[id].mx = tr[rc].mx, tr[id].id = tr[rc].id;
}
void update(int &id, int l, int r, int x, int c) {if (!id) id = ++cnt; //进行动态建点if (l == r) {tr[id].mx += c;  tr[id].id = l;return;} int mid = (l + r) >> 1;if (x <= mid) update(tr[id].lc, l, mid, x, c);else update(tr[id].rc, mid + 1, r, x, c);pushup(id);
}
int merge(int p, int q, int l, int r) {if (!p) return q; if (!q) return p;if (l == r) {tr[p].mx += tr[q].mx;return p;}int mid = (l + r) >> 1;tr[p].lc = merge(tr[p].lc, tr[q].lc, l, mid);tr[p].rc = merge(tr[p].rc, tr[q].rc, mid + 1, r);pushup(p);return p;
}
void dfs(int u, int fa) {for (int j = h[u]; j; j = e[j].next) {int v = e[j].v;if (v == fa) continue;dfs(v, u);rt[u] = merge(rt[u], rt[v], 1, cntz);}ans[u] = tr[rt[u]].mx == 0 ? 0 : tr[rt[u]].id;
}
int main() {scanf("%d%d", &n, &m);lg = int(log(n) / log(2)) + 1;for (int i = 1; i < n; i++) {scanf("%d%d", &u[0], &v[0]);add(u[0], v[0]); add(v[0], u[0]);} bfs();for (int i = 1; i <= m; i++) { scanf("%d%d%d", &u[i], &v[i], &z[i]); idz[i] = z[i];}//对z进行离散化sort(idz + 1, idz + 1 + m);cntz = unique(idz + 1, idz + 1 + m) - idz - 1;for (int i = 1; i <= m; i++) {int lca = LCA(u[i], v[i]);int zt = lower_bound(idz + 1, idz + 1 + cntz, z[i]) - idz;update(rt[u[i]], 1, cntz, zt, 1);update(rt[v[i]], 1, cntz, zt, 1);update(rt[lca], 1, cntz, zt, -1);update(rt[f[lca][0]], 1, cntz, zt, -1);       } dfs(1, 0);for (int i = 1; i <= n; i++) printf("%d\n", idz[ans[i]]);return 0;
}

P4556 雨天的尾巴 线段树合并相关推荐

  1. [Vani有约会]雨天的尾巴 (线段树合并)

    题目链接 Solution 树上差分+线段树合并. 在每个节点上维护一棵权值线段树. 然后如果需要修改 \(x,y\) 两点,则在 \(x\) 处和 \(y\) 处分别加上 \(1\) 的权值. 然后 ...

  2. 线段树合并(四道例题)

    顾名思义,就是合并两个同构(就是维护的区间长度一样)线段树,其实也没啥比较nb的算法,就是一个一个节点的合并,但是如果在n个要合并的线段树里,如果一共有m个元素,则配合动态开点,复杂度会均摊成一个惊人 ...

  3. 【线段树合并】解题报告:luogu P4556雨天的尾巴 (树上对点差分 + 动态开点 + 线段树合并)线段树合并模板离线/在线详解

    题目链接:雨天的尾巴 本题本身是一个非常简单的一道树上差分的模板题,但是由于变态的数据范围,我们直接用数组是存不下的(本来使用一颗普通的线段树直接维护最大值即可.但是本题的空间只有128MB,直接按照 ...

  4. 线段树分裂与合并 ---- 树上差分 P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

    题目链接 解题思路: 首先题目是对u,vu,vu,v这两条路径上面添加一个zzz,然后运用树上点的差分思想,对于分发路径u,vu,vu,v,我们在uuu上+1+1+1,在vvv上+1+1+1,在lca ...

  5. 洛谷 - P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并(树上差分+线段树合并)

    题目链接:点击查看 题目大意:给出一棵树,再给出 m 次操作,每次操作会选择两个点 ( x , y ) ,使得这条路径上的所有点的种类 z 加一,最后问每个点的哪个种类出现的频率最高,若多个种类出现频 ...

  6. 雨中的尾巴(线段树合并+树上差分)

    哇这道题 恶心死我 首先要知道,树上差分一般解决的问题是关于树上的覆盖问题 然后遇到覆盖问题尽量不要打树剖(会慢很多) 关于此题 因为这道题覆盖的是 从xxx到yyy的点 所以我们在 x,yx,yx, ...

  7. 线段树合并:从入门到放弃

    感谢这篇博客(这里跳转)以及邱宇大神的讲解,我也算作入门(自闭)了. 需要掌握的前置知识点:动态开点线段树.权值线段树. 一.合并思想 线段树合并,就是指建立一颗新的线段树,保存原有的两颗线段树的信息 ...

  8. 清明梦超能力者黄YY、异或树(线段树合并)

    清明梦超能力者黄YY 这题有点像[雨天的尾巴][永无乡]的结合版本,树上差分,线段树合并,权值线段树查找第kkk大. 对于操作iii,我们可以对u−>vu->vu−>v路径上的点,i ...

  9. P4556 雨天的尾巴

    题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地 ...

  10. 线段树合并与分裂维护树上最长上升子序列 + 点分治删点 ---- 2021 牛客多校第一场 C - Cut the tree(详解)

    题目大意: 给你一个树,树上每个点都有一个权值valnodeval_{node}valnode​,路径(u,v)(u,v)(u,v) 上所有点按顺序有序序列,令f(u,v)f(u,v)f(u,v)是这 ...

最新文章

  1. Struts 2框架结构图
  2. 向git库提交代码出现”There are no staged files怎么办?
  3. 玩吧高速增长的数据上云实践
  4. IDEA里的web.xml页面的Servlet名称报错下方出现红色下划线
  5. 十分钟轻松搞懂CSS的五大定位方式!(建议收藏)
  6. 超越传感器和相机 - 将物联网从云端迁移到物理世界
  7. JAVA面试要点004_JAVA编程过程中为了性能优化_应该注意到的地方
  8. CentOS下搭建测试WEB平台
  9. 网格搜索、随机搜索机器学习实战
  10. qgis中加载矢量切片
  11. 训练loss不下降原因总结
  12. 集群通信组件Tribes之整体介绍
  13. Salesforce中reRender和rendered控件的使用
  14. #CSDN软件工程师能力认证学习精选# Hadoop基础知识学习
  15. 微信小程序如何创建云开发根目录
  16. 或再被“转手”,家乐福中国还能“攀”上哪座靠山?
  17. 陶哲轩实分析习题8.3.4
  18. 好消息!ios10越狱插件造成桌面图标消失的完美解决方法来了!!!
  19. pairs 和 ipairs区别
  20. 使用scrapy框架从爬取安居客数据到分析

热门文章

  1. 北邮教授石川:图神经网络需要解决的几个关键问题
  2. 百度webuploader上传到阿里OSS
  3. convex hull
  4. java开发工程师面试自我介绍_Java程序员自我介绍
  5. robotstudio机器人应用实践(码垛搬运工作站)
  6. Linux环境变量PSI指什么,psi是什么单位(pSI指标应用原则)
  7. Row was updated or deleted by another transaction or unsaved-value mapping was incorrect解决
  8. RocketMQ 优雅停机技巧
  9. 彻底理解nth-child和nth-of-type的区别
  10. Matlab学习手记——牛顿型信頼域法求解无约束问题