Luogu 4556 雨天的尾巴
主席树+线段树合并。
首先我们想一想如果只有一个结点的话,我们弄一个权值线段树就可以随便维护了。
那么我们可以运用差分的思想,把一个询问拆成四个操作,对于一个询问$(x, y, v)$,我们在$x$的$k$处$ + 1$,在$y$的$k$处$ + 1$,在$lca(x, y)$处$ - 1$,在$fa(lca(x, y))$处$ - 1$,那么每一个点最后的权值线段树的样子就相当于把它和它的子树中的权值线段树全部合并之后得到的线段树。
动态开点就可以了。
前置技能:线段树合并。 戳这里
这样子我们往下搜一遍把每一个点和它的儿子合并,然后记录一下答案就可以了。
不会算时间复杂度QωQ。
另外,这题数据很卡,我写了内存回收 + $queue$开了$O2$才卡过。
Code:
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std; const int N = 1e5 + ;
const int Lg = ; int n, m, maxn = , tot = , head[N], ans[N];
int fa[N][Lg], dep[N], inx[N], iny[N], inv[N], val[N]; struct Edge {
int to, nxt;
} e[N << ]; inline void add(int from, int to) {
e[++tot].to = to;
e[tot].nxt = head[from];
head[from] = tot;
} struct Innum {
int val, id;
} in[N]; bool cmp(const Innum &x, const Innum &y) {
if(x.val != y.val) return x.val < y.val;
else return x.id < y.id;
} inline void swap(int &x, int &y) {
int t = x; x = y; y = t;
} inline void chkMax(int &x, int y) {
if(y > x) x = y;
} inline int min(int x, int y) {
return x > y ? y : x;
} inline void read(int &X) {
X = ; char ch = ; int op = ;
for(; ch > '' || ch < ''; ch = getchar())
if(ch == '-') op = -;
for(; ch >= '' && ch <= ''; ch = getchar())
X = (X << ) + (X << ) + ch - ;
X *= op;
} inline void discrete() {
sort(in + , in + + m, cmp);
for(int cnt = , i = ; i <= m; i++) {
if(in[i].val != in[i - ].val) ++cnt;
chkMax(maxn, cnt);
inv[in[i].id] = cnt;
val[cnt] = in[i].val;
}
} void dfs(int x, int fat, int depth) {
dep[x] = depth, fa[x][] = fat;
for(int i = ; i <= ; i++)
fa[x][i] = fa[fa[x][i - ]][i - ];
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fat) continue;
dfs(y, x, depth + );
}
} inline int getLca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = ; i >= ; i--)
if(dep[fa[x][i]] >= dep[y])
x = fa[x][i];
if(x == y) return x;
for(int i = ; i >= ; i--)
if(fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][];
} namespace PSegT {
struct Node {
int lc, rc, sum, col;
} s[N * ]; int root[N], nodeCnt = ; queue <int> Q; inline void push(int x) {
Q.push(x);
} inline int newNode() {
if(Q.empty()) return ++nodeCnt;
else {
int res = Q.front();
Q.pop();
return res;
}
} #define lc(p) s[p].lc
#define rc(p) s[p].rc
#define sum(p) s[p].sum
#define col(p) s[p].col
#define mid ((l + r) >> 1) inline void up(int p) {
if(!p) return;
if(sum(lc(p)) < sum(rc(p))) col(p) = col(rc(p)), sum(p) = sum(rc(p));
else col(p) = col(lc(p)), sum(p) = sum(lc(p));
} void modify(int &p, int l, int r, int x, int v) {
if(!p) p = newNode();
if(l == r) {
sum(p) += v;
if(sum(p) > ) col(p) = l;
else col(p) = ;
return;
} if(x <= mid) modify(lc(p), l, mid, x, v);
else modify(rc(p), mid + , r, x, v);
up(p);
} int merge(int u, int v, int l, int r) {
if(!u || !v) return u + v;
int p = newNode();
if(l == r) {
sum(p) = sum(u) + sum(v);
if(sum(p) > ) col(p) = l;
else col(p) = ;
} else {
lc(p) = merge(lc(u), lc(v), l, mid);
rc(p) = merge(rc(u), rc(v), mid + , r);
up(p);
}
push(u), push(v);
return p;
} } using namespace PSegT; void solve(int x) {
for(int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if(y == fa[x][]) continue;
solve(y);
root[x] = merge(root[x], root[y], , maxn);
} /* printf("%d: ", x);
for(int i = 1; i <= maxn; i++)
printf("%d ", query(root[id[x] - 1], root[id[x]], 1, maxn, i));
printf("\n"); */ ans[x] = val[s[root[x]].col];
} int main() {
read(n), read(m);
for(int x, y, i = ; i < n; i++) {
read(x), read(y);
add(x, y), add(y, x);
}
dfs(, , ); // maxn = 1e5; for(int i = ; i <= m; i++) {
read(inx[i]), read(iny[i]), read(inv[i]);
in[i].id = i, in[i].val = inv[i];
}
discrete(); for(int x, y, v, z, w, i = ; i <= m; i++) {
x = inx[i], y = iny[i], v = inv[i];
z = getLca(x, y), w = fa[z][]; /* vec[x].push_back(pin(v, 1));
vec[y].push_back(pin(v, 1));
vec[z].push_back(pin(v, -1));
if(w) vec[w].push_back(pin(v, -1)); */ modify(root[x], , maxn, v, );
modify(root[y], , maxn, v, );
modify(root[z], , maxn, v, -);
if(w) modify(root[w], , maxn, v, -);
} /* printf("\n");
for(int i = 1; i <= n; i++) {
printf("%d: ", i);
for(int j = 1; j <= maxn; j++)
printf("%d ", query(root[id[i] - 1], root[id[i] + siz[i] - 1], 1, maxn, j));
printf("\n");
}
printf("\n"); */ solve(); for(int i = ; i <= n; i++)
printf("%d\n", ans[i]); return ;
}
Luogu 4556 雨天的尾巴的更多相关文章
- Luogu 4556 雨天的尾巴 - 启发式合并线段树
Solution 用$col$记录 数量最多的种类, $sum$记录 种类$col$ 的数量. 然后问题就是树上链修改, 求 每个节点 数量最多的种类. 用树上差分 + 线段树合并更新即可. Code ...
- [luogu4556]雨天的尾巴
[luogu4556]雨天的尾巴 luogu 发现是一顿子修改然后再询问,那么把修改树上差分一下再线段树合并 但是... 如果你只有35分... https://www.luogu.org/discu ...
- P4556 [Vani有约会]雨天的尾巴(线段树合并+lca)
P4556 [Vani有约会]雨天的尾巴 每个操作拆成4个进行树上差分,动态开点线段树维护每个点的操作. 离线处理完向上合并就好了 luogu倍增lca被卡了5分.....于是用rmq维护.... 常 ...
- BZOJ 3307: 雨天的尾巴( LCA + 线段树合并 )
路径(x, y) +z : u处+z, v处+z, lca(u,v)处-z, fa(lca)处-z, 然后dfs一遍, 用线段树合并. O(M log M + M log N). 复杂度看起来不高, ...
- BZOJ_3307_雨天的尾巴_线段树合并+树上差分
BZOJ_3307_雨天的尾巴_线段树合并 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后 ...
- [Vani有约会]雨天的尾巴 线段树合并
[Vani有约会]雨天的尾巴 LG传送门 线段树合并入门好题. 先别急着上线段树合并,考虑一下这题的暴力.一看就是树上差分,对于每一个节点统计每种救济粮的数量,再一遍dfs把差分的结果统计成答案.如果 ...
- 【BZOJ 3307】 3307: 雨天的尾巴 (线段树+树链剖分)
3307: 雨天的尾巴 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 458 Solved: 210 Description N个点,形成一个树状结 ...
- 洛谷 P4556 [Vani有约会]雨天的尾巴 解题报告
P4556 [Vani有约会]雨天的尾巴 题目背景 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒 ...
- 【BZOJ3307】雨天的尾巴 线段树合并
[BZOJ3307]雨天的尾巴 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多 ...
随机推荐
- hd acm2025
问题:平面上有n条折线,问这些折线最多能将平面分割成多少块? 思路:像这种平面被线段分割成几部分的问题,80%用递推解决,因为n条线段与(n-1)条线段能建立联系. 你可以作图观察一下,会发现新增 ...
- 【JavaScript学习整理】DOM对象(location history screen navigator)
DOM: 描述网页各个组成部分之间的关系. parentNode: 父节点 childNode: 子节点 firstChild: 第一个子节点 lastChild: 最后一个子节点 nextSibli ...
- 本地建立SVN服务器
想在自己电脑上搭建SVN服务器,于是有以下步骤. 首先明确SVN服务包括服务器和客户端,平时听到的TortoiseSVN就是一个客户端. 首先下载两个软件,服务器端我使用的是VisualSVN,版本是 ...
- github与eclipse结合使用
github是现在流行的代码托管平台,今天以eclipse为例讲解github的使用,新建项目,提交eclipse项目到github,新建分支,合并分支 1.github上新建项目 记下githug项 ...
- fffmpeg 提取pcm
ffmpeg -i input.aac -codec:a pcm_f32le -ar 48000 -ac 2 -f f32le output.pcm
- 关于float与double区别
Problem A: 啤酒和饮料 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 175 Solved: 29 [Submit][Status][We ...
- Java8中聚合操作collect、reduce方法详解
Stream的基本概念 Stream和集合的区别: Stream不会自己存储元素.元素储存在底层集合或者根据需要产生.Stream操作符不会改变源对象.相反,它会返回一个持有结果的新的Stream.3 ...
- SQLite优化方法
1.建表优化 SQLite的数据库本质文件读写操作,频繁操作打开和关闭是很耗时和浪费资源的: 优化方法事务机制: 这里要注意一点:事务的开启是要锁定DB的,其他对DB的写入操作都是无法成功的. db. ...
- IE input 去掉文本框的叉叉和密码输入框的眼睛图标
::-ms-clear, ::-ms-reveal{display: none;}
- 20179215《Linux内核原理与分析》第一周作业
一.Linux介绍 我们现在很常见Windows系统,对于Linux则显得尤为陌生.当然我也不例外,初识Linux过程中遇到一些困惑,但我也在实验的同时通过不断查找资料与实践中慢慢解决问题.那么下面我 ...