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 雨天的尾巴 - 启发式合并线段树的更多相关文章

  1. Luogu 4556 雨天的尾巴

    主席树+线段树合并. 首先我们想一想如果只有一个结点的话,我们弄一个权值线段树就可以随便维护了. 那么我们可以运用差分的思想,把一个询问拆成四个操作,对于一个询问$(x, y, v)$,我们在$x$的 ...

  2. 【bzoj3307】雨天的尾巴 权值线段树合并

    题目描述 N个点,形成一个树状结构.有M次发放,每次选择两个点x,y,对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成所有发放后,每个点存放最多的是哪种物品. 输入 第一行数字N,M接下来 ...

  3. bzoj3307 雨天的尾巴 题解(线段树合并+树上差分)

    Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后,每个点存放最多的是哪种物品. Input ...

  4. BZOJ 3307 雨天的尾巴 (树上差分+线段树合并)

    题目大意:给你一棵树,树上一共n个节点,共m次操作,每次操作给一条链上的所有节点分配一个权值,求所有节点被分配到所有的权值里,出现次数最多的权值是多少,如果出现次数相同就输出最小的. (我辣鸡bzoj ...

  5. 启发式合并&线段树合并/分裂&treap合并&splay合并

    启发式合并 有\(n\)个集合,每次让你合并两个集合,或询问一个集合中是否存在某个元素. ​ 我们可以用平衡树/set维护集合. ​ 对于合并两个\(A,B\),如果\(|A|<|B|\),那么 ...

  6. [BZOJ2733][HNOI2010]永无乡 解题报告 启发式合并,线段树合并

    好久没更新博客了,前段时间一直都在考试,都没时间些,现在终于有点闲了(cai guai)... 写了一道题,[HNOI2012]永无乡,其实是一道板子题,我发现我写了好多板子题...还是太菜了... ...

  7. bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)

    这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例, ...

  8. HDU - 4358 Boring counting (树上启发式合并/线段树合并)

    题目链接 题意:统计树上每个结点中恰好出现了k次的颜色数. dsu on tree/线段树合并裸题. 启发式合并1:(748ms) #include<bits/stdc++.h> usin ...

  9. 5.20 省选模拟赛 T1 图 启发式合并 线段树合并 染色计数问题

    LINK:图 在说这道题之前吐槽一下今天的日子 520 = 1+1+4+514. /cy 这道题今天做的非常失败 一点分都没拿到手 关键是今天的T3 把我整个人给搞崩了. 先考虑 如果得到了这么一张图 ...

随机推荐

  1. vue 异步请求

    摘自 ECMAScript 6 简介: 大家习惯将 ECMAScript 6.0 简称为 ES6,它是 Javascript 语言的下一代标准,它的目标,是使得 Javascript 语言可以用来编写 ...

  2. rectangle,boundingRect和Rect

    rectangle( rook_image, Point( , *w/8.0 ), Point( w, w), Scalar( , , ), , ); 矩形将被画到图像 rook_image 上 矩形 ...

  3. unity中Android环境变量配置

    http://www.cnblogs.com/windytrees/p/7533477.html

  4. centos静态绑定IP地址

    Centos7 /etc/sysconfig/network-scripts/ifcfg-ens33

  5. 解题(StringJiaMi--字符串加密)

    题目描述 有一种技巧可以对数据进行加密,它使用一个单词作为它的密匙.下面是它的工作原理:首先,选择一个单词作为密匙,如TRAILBLAZERS.如果单词中包含有重复的字母,只保留第1个,其余几个丢弃. ...

  6. javax.servlet.http.HttpServletResponse.getStatus()I

    感谢作者分享:http://blog.csdn.net/szwangdf/article/details/42145463 -------------------------------------- ...

  7. selenium ide界面介绍

    Selenium Ide是firefox浏览器的一个插件,可以进行web的录制和回放,完成简单的自动化测试,同时可以将录制的脚本导出多种语言的脚本. 下面是Selenium Ide的界面: Base  ...

  8. PHP实现curl和snoopy类模拟登陆方法

    Snoopy.class.php下载 方法/步骤   第一种:使用snoopy类实现模拟登陆 1.在网上下载一个Snoopy.class.php的文件   2.代码实现: <?php set_t ...

  9. 纯css3棋盘图案背景以及45度斜纹背景

    css代码  .stripes {     height: 250px;     width: 375px;     float: left;          margin: 10px;      ...

  10. STL-queue和循环队列基本操作的实现

    2018-11-13-17:53:44 1.可增长循环队列 队列是一种特殊的线性表,是一种先进先出(FIFO)的数据结构.它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入 ...