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. RabbitMQ的安装与管理控制台设置

    首先下载安装Erlang环境:http://www.erlang.org/downloads: 再下载安装RabbitMQ:http://www.rabbitmq.com/download.html: ...

  2. 微信小程序---导航

    1.wx.navigateTo(OBJECT):保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack可以返回到原页面. wx.navigateTo({ url: 'test?id= ...

  3. elasticsearch 问题

    elasticsearch 的端口默认绑定到 127.0.0.1 上,对外开放 http 端口就配置 http.host,对外开放 tcp 端口就配置 network.host [1]: max fi ...

  4. elasticsearch 测试

    https://www.yiibai.com/elasticsearch/elasticsearch-getting-start.html # curl -XPUT "http://loca ...

  5. linux mysqlERROR 1045 (28000): linux忘记数据库密码

    已验证没问题 #1.停止mysql数据库(确定能停止掉,不然第二部有问题) /etc/init.d/mysqld stop   #2.执行如下命令 mysqld_safe --user=mysql - ...

  6. ssh架构之hibernate(三)关系映射

    1.单向多对一 1.映射文件配置 2.model: 测试 1.查询测试 执行顺序,先查询多方,在查询一方,一方采用延迟加载 注意:如果不使用一方的数据,就关闭session,报错,同延迟加载中的报错类 ...

  7. MySql数据库常用语句汇总

    第一天1.登陆数据库 mysql -uroot -proot; //-u用户名 -p密码2.启动数据库 net start mysql;3.创建表空间(数据库)create database qy97 ...

  8. extJS 动态引用加载(转)

    ExtJs有庞大的类型库,很多类可能在当前的页面根本不会用到,我们可以引入动态加载的概念来即用即取.这些代码都要写在Ext.onReady外面. 1.动态引用外部Js //加载配置可用 Ext.Loa ...

  9. 基本数据类型(list,tuple)

    基本数据类型(list,tuple)内容: 1. 列表2. 列表的增删改查3. 列表的嵌套4. 元组和元组嵌套5. range⼀. 列表1.1 列表的介绍列表是python的基础数据类型之⼀,其他编程 ...

  10. pta l2-5(集合相似度)

    题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805070149828608 题意:求两个集合的相同的不同元素的 ...