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. java-学习10

    使用return结束一个方法 public class function3 { public static void main(String[] args) { System.out.println( ...

  2. 从 mvc 到 REST

    1. 理解MVC MVC是一种经典的设计模式,全名为Model-View-Controller,即模型-视图-控制器. 其中,模型是用于封装数据的载体,例如,在Java中一般通过一个简单的POJO(P ...

  3. Steering Behaviors

    [Steering Behaviors] 1.Seek 下述的算法是一个基本Seek行为,但不带任何Steering输出的力.在该公式作用下,游戏个体的移动方式是直线型的,如果target的位置变了的 ...

  4. iframe解决ajax主域和子域之间的跨域问题

    在某些应用场景下,需要在主域中,调用子域中的某个接口,如果直接在主域中向子域发ajax请求,会报跨域错误,可以用iframe来解决这种跨域问题.假如主域为www.baidu.com,子域为baike. ...

  5. JMeter学习(二十六)逻辑控制器(转载)

    转载自 http://www.cnblogs.com/yangxia-test JMeter中的Logic Controller用于为Test Plan中的节点添加逻辑控制器. JMeter中的Log ...

  6. 算法之LOWB三人组之插入排序

    插入排序 思想:类似于抽扑克牌,共有8张扑克牌,手里默认有一张,桌面上有7张,我们每次从桌面上抽一张和手里的牌进行比较,如果比手里的牌大,则直接放到手里的牌的后面,如果比手里的牌小,则放到手里的牌的前 ...

  7. 【selenium+python】关于使用selenium时的几个问题1

    问题:selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in ...

  8. rancher 2 webhook 格式

    { "version":"4", "groupKey":<string>, "status":"& ...

  9. cdh 5.13 hadoop 集群IP变更详细步骤

    1.因一些不可抗因素,集群IP变更. 修改CM的数据库IP地址 /etc/cloudera-scm-server/db.p... 2.修改每个主机的hosts列表 3.修改SCM数据库的hosts表中 ...

  10. codeblocks17.12 不能启动调试器

    调试器需要手动指定. settings->debugger->default->executable path.这里默认空的,需要指定.路径在安装目录下的CodeBlocks\Min ...