Luogu 4556 雨天的尾巴 - 启发式合并线段树
Solution
用$col$记录 数量最多的种类, $sum$记录 种类$col$ 的数量。
然后问题就是树上链修改, 求 每个节点 数量最多的种类。
用树上差分 + 线段树合并更新即可。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rd read()
using namespace std; const int N = 1e5 + ; int head[N], cnt;
int n, m;
int sum[N], top[N], son[N], sz[N], f[N], dep[N];
int u[N], v[N], z[N], b[N], tot, ans[N], idf[N]; struct edge {
int nxt, to;
}e[N << ]; int read() {
int X = , p = ; char c = getchar();
for (; c > '' || c < ''; c = getchar())
if (c == '-') p = -;
for (; c >= '' && c <= ''; c = getchar())
X = X * + c - '';
return X * p;
} void add(int u, int v) {
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
} int fd(int x) {
return lower_bound(b + , b + + tot, x) - b;
} namespace SegT { struct node {
int sum, col, lson, rson;
node() {
sum = col = lson = rson = ;
}
} s[N * ]; int root[N]; #define lc(p) s[p].lson
#define rc(p) s[p].rson
#define sum(p) s[p].sum
#define col(p) s[p].col
#define mid ((l + r) >> 1) int st[N * ], tp, qnum; int get() {
if (tp) {
int re = st[tp];
tp--;
return re;
} else return ++qnum;
} void del(int x) {
lc(x) = rc(x) = sum(x) = col(x) = ;
st[++tp] = x;
} void up(int p) {
if(sum(lc(p)) >= sum(rc(p)))
sum(p) = sum(lc(p)), col(p) = col(lc(p));
else
sum(p) = sum(rc(p)), col(p) = col(rc(p));
} void modify(int l, int r, int pos, int d, int &x) {
if(!x) x = get();
if (l == r) {
sum(x) += d;
if (sum(x) > )
col(x) = pos;
return;
}
if (pos <= mid)
modify(l, mid, pos, d, lc(x));
else
modify(mid + , r, pos, d, rc(x));
up(x);
} int merge(int l, int r, int x, int y) {
if (!x || !y)
return x + y;
int now = get();
if (l == r) {
sum(now) = sum(x) + sum(y);
if (sum(now) > )
col(now) = max(col(x), col(y));
else col(now) = ;
}
else {
lc(now) = merge(l, mid, lc(x), lc(y));
rc(now) = merge(mid + , r, rc(x), rc(y));
up(now);
}
del(x); del(y);
return now;
}
} using namespace SegT; namespace SP {
void dfs1(int x) {
sz[x] = ;
for (int i = head[x]; i; i = e[i].nxt) {
int nt = e[i].to;
if (nt == f[x])
continue;
f[nt] = x;
dep[nt] = dep[x] + ;
SP::dfs1(nt);
sz[x] += sz[nt];
if (sz[nt] > sz[son[x]])
son[x] = nt;
}
} void dfs2(int x) {
if (!son[x])
return;
top[son[x]] = top[x];
SP::dfs2(son[x]);
for (int i = head[x]; i; i = e[i].nxt) {
int nt = e[i].to;
if (nt == f[x] || nt == son[x])
continue;
top[nt] = nt;
SP::dfs2(nt);
}
} int LCA(int x, int y) {
for (; top[x] != top[y];) {
if (dep[top[x]] < dep[top[y]])
swap(x, y);
x = f[top[x]];
}
if (dep[x] < dep[y])
swap(x, y);
return y;
} void solve(int x) {
for (int i = head[x]; i; i = e[i].nxt) {
int nt = e[i].to;
if (nt == f[x])
continue;
SP::solve(nt);
root[x] = merge(, tot, root[x], root[nt]);
}
ans[x] = col(root[x]);
}
} int main()
{
n = rd; m = rd;
for (int i = ; i < n; ++i) {
int x = rd, y = rd;
add(x, y); add(y, x);
}
dep[] = ;
SP::dfs1();
top[] = ;
SP::dfs2();
for (int i = ; i <= m; ++i) {
u[i] = rd; v[i] = rd;
z[i] = b[i] = rd;
}
tot = m;
sort(b + , b + + tot);
tot = unique(b + , b + + tot) - b - ;
for (int i = ; i <= tot; ++i)
idf[b[i]] = i;
for (int i = ; i <= m; ++i) {
int lca = SP::LCA(u[i], v[i]);
modify(, tot, idf[z[i]], , root[u[i]]);
modify(, tot, idf[z[i]], , root[v[i]]);
modify(, tot, idf[z[i]], -, root[lca]);
modify(, tot, idf[z[i]], -, root[f[lca]]);
}
SP::solve();
for (int i = ; i <= n; ++i)
printf("%d\n" ,b[ans[i]]);
}
Luogu 4556 雨天的尾巴 - 启发式合并线段树的更多相关文章
- Luogu 4556 雨天的尾巴
主席树+线段树合并. 首先我们想一想如果只有一个结点的话,我们弄一个权值线段树就可以随便维护了. 那么我们可以运用差分的思想,把一个询问拆成四个操作,对于一个询问$(x, y, v)$,我们在$x$的 ...
- 【bzoj3307】雨天的尾巴 权值线段树合并
题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y,对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入 第一行数字N,M接下来 ...
- bzoj3307 雨天的尾巴 题解(线段树合并+树上差分)
Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后,每个点存放最多的是哪种物品. Input ...
- BZOJ 3307 雨天的尾巴 (树上差分+线段树合并)
题目大意:给你一棵树,树上一共n个节点,共m次操作,每次操作给一条链上的所有节点分配一个权值,求所有节点被分配到所有的权值里,出现次数最多的权值是多少,如果出现次数相同就输出最小的. (我辣鸡bzoj ...
- 启发式合并&线段树合并/分裂&treap合并&splay合并
启发式合并 有\(n\)个集合,每次让你合并两个集合,或询问一个集合中是否存在某个元素. 我们可以用平衡树/set维护集合. 对于合并两个\(A,B\),如果\(|A|<|B|\),那么 ...
- [BZOJ2733][HNOI2010]永无乡 解题报告 启发式合并,线段树合并
好久没更新博客了,前段时间一直都在考试,都没时间些,现在终于有点闲了(cai guai)... 写了一道题,[HNOI2012]永无乡,其实是一道板子题,我发现我写了好多板子题...还是太菜了... ...
- bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)
这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例, ...
- HDU - 4358 Boring counting (树上启发式合并/线段树合并)
题目链接 题意:统计树上每个结点中恰好出现了k次的颜色数. dsu on tree/线段树合并裸题. 启发式合并1:(748ms) #include<bits/stdc++.h> usin ...
- 5.20 省选模拟赛 T1 图 启发式合并 线段树合并 染色计数问题
LINK:图 在说这道题之前吐槽一下今天的日子 520 = 1+1+4+514. /cy 这道题今天做的非常失败 一点分都没拿到手 关键是今天的T3 把我整个人给搞崩了. 先考虑 如果得到了这么一张图 ...
随机推荐
- vue 安装sass扩展
1.创建一个基于 webpack 模板的新项目 $ vue init webpack myvue 1 2.在当前目录下,安装依赖 $ cd myvue $ npm install 1 2 3.安装sa ...
- 分享一个 Java String split 快速分割的方法
java中string.split() 方法比较强大,但是split()方法采用正则表达式,速度相对会慢一点, 其实大多数场景下并不需要使用正则表达式,下面分享一个不使用正则表达式分隔字符串的方法. ...
- cdnbest如何检查https证书是否有效
注意: 用此方法检查ssl证书是否有效,此帐号下必须有有效的cdn节点,因为这个证书是要通过底层的cdn节点来检测的 1. 在站点设置中如下图点打开添加ssl证书 2.加完证书后点检查,打勾就表示证书 ...
- 使用fckeditor上传多张图片
流程: 1.使用fck上传图片到后台 2.后台上传图片到服务器端 3.服务器端返回上传信息 1.jsp页面 <script type="text/javascript"> ...
- Tcp/Ip 三次握手与四次挥手
1. TCP/IP模型 我们一般知道OSI的网络参考模型是分为7层:“应表会传网数物”——应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.而实际的Linux网络层协议是参照了OSI标准,但 ...
- MySQL数据类型及使用场景
MySQL数据类型介绍 整数类型 类型名称 说明 存储需求 取值范围有符号 取值范围符号 TINYINT 很小的整数 1个字节 -128~127 0-255 SMALLINT 小的整数 2个字节 32 ...
- as3.0两点之间简单的运动,斜着运动,任意两点
import flash.utils.Timer;import flash.events.TimerEvent;//fixed结束点//sprite初始点var fixedX:Number = fix ...
- CentOS 查找某个软件安装路径
1.通过rpm查看 查看软件是否安装.首先我们需要查看软件是否已经安装,或者说查看安装的软件包名称.如查找是否安装mysql 2.接着根据 rpm -ql 列出软件包安装的文件 3.综合上述以上的问题 ...
- 牛客练习赛15A-吉姆的运算式(Python正则表达式瞎搞)
传送门 题意:出现的数字,取最后一个数字即可. Python正则表达式提取数字 代码: import re str = input() a = re.findall(r'\-*\d+(?:\.\d+) ...
- 203. Remove Linked List Elements (List)
Remove all elements from a linked list of integers that have value val. ExampleGiven: 1 --> 2 --& ...