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 把我整个人给搞崩了. 先考虑 如果得到了这么一张图 ...
随机推荐
- linux下的arm汇编程序
1.gnu 的编译环境搭建 解压编译工具,加入环境变量PATH 2.编译相关命令的使用 编译命令 arm-linux-gcc -g -c -o led.o main.o led.c main.c / ...
- gradle 很好用的么
Gradle 其实是很好用的 2017, Apr 14 by Tesla Ice Zhang Gradle 是一款使用 Kotlin (划掉) Groovy 编写的 JVM 构建工具,其易用性和 Ma ...
- java-学习3(jdk-环境配置)
学习java先安装jdk环境配置 https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.htm ...
- GO注释
1.GO资源简介 由于生物系统的惊人复杂性和需要分析的数据集的不断增加,生物医学研究越来越依赖于以可计算的形式存储的知识.基因本体论(GO)项目为基因功能和基因产物的可计算知识提供了目前最全面的资源. ...
- fs.watchFile
[fs.watchFile] fs.watchFile(filename[, options], listener) Watch for changes on filename. The callba ...
- 命令行执行jenkins,构建job(可传递参数)
背景| 组内做UI测试,需要每天晚上执行一遍jenkins任务,jenkins任务本身是参数化构建的.但是因为jenkins本身的定时执行没有办法指定特殊的参数,所以考虑使用命令行方式启动jenkin ...
- FileReader.FileWriter 执行文本复制
//导包动作必须做,否则会出现大片错误提示 import java.io.*; class FileReaderDemo { publicstatic void main(String[] args) ...
- ucore-lab1-练习3report
练习3.分析bootload进入保护模式的过程 0. BIOS通过读取硬盘主引导扇区到内存,并跳转到对应内存中的位置,也就是从’%cs=0 $pc=0x7c00‘进入并执行bootloader,boo ...
- 160. Intersection of Two Linked Lists (List;Two-Pointers)
Write a program to find the node at which the intersection of two singly linked lists begins. For ex ...
- jQuery禁止Ajax请求缓存
一 现象 get请求在有些浏览器中会缓存.浏览器不会发送请求,而是使用上次请求获取到的结果. post请求不会缓存.每次都会发送请求. 二 解决 jQuery提供了禁止Ajax请求缓存的方法: $.a ...