洛谷P4556 雨天的尾巴(线段树合并)
洛谷P4556 雨天的尾巴
题解:
因为一个点可能存放多种物品,直接开二维数组进行统计时间、空间复杂度都不能承受。因为每一个点所拥有的物品只与其子树中的点有关,所以可以考虑对每一个点来建立一颗权值线段树来维护多种物品以及其数量,然后最后在回溯时合并,这样就可以得到我们所需要的信息了。
因为题目中要求的是哪一种物品,所以我们可以顺带维护一下位置信息,就不用到时候每次去query了。
注意一下,就是当一个点的sum为0时,其pos应该为置为0。
详见代码吧:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, m;
struct Edge{
int v, next;
}e[N << 1];
int head[N], tot, D;
void adde(int u, int v) {
e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
}
int f[N][22], deep[N] ;
int rt[N], ls[N * 100], rs[N * 100], pos[N * 100], sum[N * 100] ;
int X[N], Y[N], Z[N], b[N], ans[N];
void dfs1(int u, int fa) {
deep[u] = deep[fa] + 1;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].v;
if(v == fa) continue ;
f[v][0] = u;
for(int j = 1; j <= 20; j++) f[v][j] = f[f[v][j - 1]][j - 1] ;
dfs1(v, u) ;
}
}
int LCA(int x, int y) {
if(deep[x] < deep[y]) swap(x, y);
for(int i = 20; i >= 0; i--) {
if(deep[f[x][i]] >= deep[y]) x = f[x][i] ;
}
if(x == y) return x;
for(int i = 20; i >= 0; i--) {
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i] ;
}
return f[x][0] ;
}
void insert(int o, int l, int r, int val, int sign) {
if(l == r) {
sum[o] += sign;
pos[o] = sum[o] > 0 ? l : 0;
return ;
}
int mid = (l + r) >> 1;
if(val <= mid) {
if(!ls[o]) ls[o] = ++tot;
insert(ls[o], l, mid, val, sign) ;
} else {
if(!rs[o]) rs[o] = ++tot;
insert(rs[o], mid + 1, r, val, sign) ;
}
sum[o] = max(sum[ls[o]], sum[rs[o]]) ;
pos[o] = sum[ls[o]] >= sum[rs[o]] ? pos[ls[o]] : pos[rs[o]];
}
int merge(int x, int y, int l, int r) {
if(!x) return y;
if(!y) return x;
if(l == r) {
sum[x] += sum[y] ;
pos[x] = sum[x] > 0 ? l : 0;
return x;
}
int mid = (l + r) >> 1;
ls[x] = merge(ls[x], ls[y], l, mid) ;
rs[x] = merge(rs[x], rs[y], mid + 1, r) ;
sum[x] = max(sum[ls[x]], sum[rs[x]]) ;
pos[x] = sum[ls[x]] >= sum[rs[x]] ? pos[ls[x]] : pos[rs[x]] ;
return x;
}
void dfs2(int u, int fa) {
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v == fa) continue ;
dfs2(v, u) ;
rt[u] = merge(rt[u], rt[v], 1, D) ;
}
ans[u] = pos[rt[u]];
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
memset(head, -1, sizeof(head)) ;
for(int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
adde(u, v); adde(v, u);
}
dfs1(1, 0) ;
for(int i = 1; i <= n; i++) rt[i] = i;
tot = n;
for(int i = 1; i <= m; i++) {
cin >> X[i] >> Y[i] >> Z[i] ;
b[i] = Z[i] ;
}
sort(b + 1, b + m + 1);
D = unique(b + 1, b + m + 1) - b - 1;
for(int i = 1; i <= m; i++) {
int x = X[i], y = Y[i], z = Z[i] ;
int k = lower_bound(b + 1, b + D + 1, z) - b;
int lca = LCA(x, y) ;
insert(rt[x], 1, D, k, 1) ;
insert(rt[y], 1, D, k, 1) ;
insert(rt[lca], 1, D, k, -1) ;
if(f[lca][0]) insert(rt[f[lca][0]], 1, D, k, -1) ;
}
dfs2(1, 0) ;
for(int i = 1; i <= n; i++) cout << b[ans[i]] << '\n' ;
return 0;
}
洛谷P4556 雨天的尾巴(线段树合并)的更多相关文章
- 洛谷P4556 雨天的尾巴 线段树
正解:线段树合并 解题报告: 传送门! 考虑对树上的每个节点开一棵权值线段树,动态开点,记录一个max(num,id)(这儿的id,define了一下,,,指的是从小到大排QAQ 然后修改操作可以考虑 ...
- P4556 雨天的尾巴 线段树合并
使用线段树合并,每个节点维护一棵权值线段树,下标为救济粮种类,区间维护数量最多的救济粮编号(下标).所以每个节点答案即为\(tre[rot[x]]\). 然后运用树上点的差分思想,对于分发路径\(u, ...
- [洛谷P4556] 雨天的尾巴
这道题可以用线段树合并做,网上的题解基本上都是线段树合并的. 但是为什么我就偏偏要用dsu on tree...... 题目传送门 dsu on tree的方法类似[CF1009F] Dominant ...
- [洛谷 P4556] 雨天的尾巴
传送门 Solution 线段树合并的入门题 lca可以在dfs的时候离线求(用并查集) 更新的点有每条链的两个端点,它们的lca和dad[lca] 为了节省空间,lca和dad[lca]的更新可以先 ...
- [Vani有约会]雨天的尾巴 线段树合并
[Vani有约会]雨天的尾巴 LG传送门 线段树合并入门好题. 先别急着上线段树合并,考虑一下这题的暴力.一看就是树上差分,对于每一个节点统计每种救济粮的数量,再一遍dfs把差分的结果统计成答案.如果 ...
- 【BZOJ3307】雨天的尾巴 线段树合并
[BZOJ3307]雨天的尾巴 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多 ...
- 洛谷P3899 [湖南集训]谈笑风生(线段树合并)
题意 题目链接 Sol 线段树合并板子题,目前我看到两种写法,分别是这样的. 前一种每次需要新建一个节点,空间是\(O(4nlogn)\) 后者不需要新建,空间是\(O(nlogn)\)(面向数据算空 ...
- BZOJ3307雨天的尾巴——线段树合并
题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入 第一行数字N,M接下来N ...
- 洛谷P3521 [POI2011]ROT-Tree Rotation [线段树合并]
题目传送门 Tree Rotation 题目描述 Byteasar the gardener is growing a rare tree called Rotatus Informatikus. I ...
随机推荐
- vue本地静态图片的路径问题解决方案
不少人在vue的开发中遇到这样一个问题: img的src属性绑定url变量,然而图片加载失败. 大部分的情况中,是开发者使用了错误的写法,例如: <img src="{{ imgUrl ...
- Python(三)对装饰器的理解
装饰器是 Python 的一个重要部分,也是比较难理解和使用好的部分.下面对装饰器做一下简单整理 1. 前言 装饰器实际上是应用了设计模式里,装饰器模式的思想: 在不概念原有结构的情况下,添加新的功能 ...
- Yarn和Zookeeper的区别
Yarn:分布式资源管理器 Zookeeper:分布式协作服务 Zookeeper是一个分布式协调服务(Coordination),一个leader,多个follower组成的集群,就是为用户的分布式 ...
- Solr7.x学习(7)-JAVA操作
maven依赖 <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-sol ...
- javascript中的each遍历
each的用法 1.数组中的each 复制代码 var arr = [ "one", "two", "three", "four ...
- IO流一些问题的总结
字节流的继承体系 字符流的继承体系 字符编码是什么?常见的字符编码表有哪些? 字符编码(英语:Character encoding)也称字集码,是把字符集中的字符编码为指定集合中某一对象,以便文本在计 ...
- 用eclipse开发需要准备什么?
1.到eclipse的官网上,https://www.eclipse.org/ 下载好eclipse,安装好eclipse,修改eclipse.ini文件,把内存改大点,避免出现内存溢出的情况. [ ...
- StringToKenizer和Scanner的区别
相同点: StringToKenizer类和Scanner类都可用于分解字符序列中的单词! 不同点: StringToKenizer类把分解出的全部字符串都存放到StringToKenizer对象的实 ...
- ElasticSearch中碰到的C10K问题
Elasticsearch基于Netty解决C10K问题背后的原理是JAVA NIO中的IO多路复用机制,涉及到三大"组件":SelectableChannel.Selector. ...
- vue nexttick的理解和使用场景
应用场景 需要在视图更新之后,基于新的视图进行操作 文档说明 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM nextTick原理 1.异步说明 V ...