【CF600E】Lomsat gelral——树上启发式合并
(题面来自luogu)
题意翻译
一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。
ci <= n <= 1e5
裸题。统计时先扫一遍得到出现次数最大值,然后再扫一遍看哪个颜色的出现次数与mxCnt相等。注意用一个bool数组判重,清空轻儿子贡献时要顺手把bool数组也打成false。(这是错的,请看下一份代码)
代码:
- #include <iostream>
- #include <cstdio>
- #include <cctype>
- #define maxn 100010
- using namespace std;
- template <typename T>
- void read(T &x) {
- x = 0;
- char ch = getchar();
- while (!isdigit(ch))
- ch = getchar();
- while (isdigit(ch))
- x = x * 10 + (ch ^ 48), ch = getchar();
- }
- int n;
- int head[maxn], top;
- struct E {
- int to, nxt;
- } edge[maxn << 1];
- inline void insert(int u, int v) {
- edge[++top] = (E) {v, head[u]};
- head[u] = top;
- }
- int son[maxn], size[maxn];
- long long ans[maxn];
- void dfs1(int u, int pre) {
- size[u] = 1;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v == pre) continue;
- dfs1(v, u);
- size[u] += size[v];
- if (size[v] > size[son[u]])
- son[u] = v;
- }
- return;
- }
- int color[maxn], cnt[maxn], sum;
- int curSon, mxCnt;
- bool vis[maxn];
- void calc(int u, int pre, bool op) {
- op ? (++cnt[color[u]], mxCnt = max(mxCnt, cnt[color[u]])) : (--cnt[color[u]], vis[color[u]] = false);
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v == pre || v == curSon)
- continue;
- calc(v, u, op);
- }
- }
- void stats(int u, int pre) {
- if (cnt[color[u]] == mxCnt && !vis[color[u]])
- sum += color[u], vis[color[u]] = true;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v != pre)
- stats(v, u);
- }
- return;
- }
- void dsu(int u, int pre, bool op) {
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v == pre || v == son[u])
- continue;
- dsu(v, u, 0);
- }
- if (son[u])
- dsu(son[u], u, 1), curSon = son[u];
- mxCnt = 0;
- calc(u, pre, 1);
- stats(u, pre);
- ans[u] = sum;
- curSon = 0;
- if (!op) {
- sum = 0;
- calc(u, pre, 0);
- }
- }
- int main() {
- read(n);
- for (int i = 1; i <= n; ++i)
- read(color[i]);
- int u, v;
- for (int i = 1; i < n; ++i) {
- read(u), read(v);
- insert(v, u), insert(u, v);
- }
- dfs1(1, 0);
- dsu(1, 0, 1);
- for (int i = 1; i <= n; ++i)
- printf("%I64d ", ans[i]);
- return 0;
- }
(交这个题的时候cf炸了……才不是因为我TLE呢
--------------------------
然而果然出了锅,昨天CF评测机大概是被这个弱智错误笑死的……
重测了下,上面那份代码是错的,原因是没有考虑重儿子内的最大出现次数。然而原思路越改越复杂,后来想了想,每遍历到重复的颜色其出现次数都会+1,因此不用判重,从每个点统计时扫一遍就好了。
代码:
- #include <iostream>
- #include <cstdio>
- #include <cctype>
- #define maxn 100010
- using namespace std;
- template <typename T>
- void read(T &x) {
- x = 0;
- char ch = getchar();
- while (!isdigit(ch))
- ch = getchar();
- while (isdigit(ch))
- x = x * 10 + (ch ^ 48), ch = getchar();
- }
- int n;
- int head[maxn], top;
- struct E {
- int to, nxt;
- } edge[maxn << 1];
- inline void insert(int u, int v) {
- edge[++top] = (E) {v, head[u]};
- head[u] = top;
- }
- int son[maxn], size[maxn];
- long long ans[maxn];
- void dfs1(int u, int pre) {
- size[u] = 1;
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v == pre) continue;
- dfs1(v, u);
- size[u] += size[v];
- if (size[v] > size[son[u]])
- son[u] = v;
- }
- return;
- }
- int color[maxn], cnt[maxn];
- long long sum;
- int curSon, mxCnt;
- void calc(int u, int pre, int val) {
- cnt[color[u]] += val;
- if (val == 1) {
- if (cnt[color[u]] > mxCnt)
- mxCnt = cnt[color[u]], sum = color[u];
- else if (cnt[color[u]] == mxCnt)
- sum += color[u];
- }
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v == pre || v == curSon)
- continue;
- calc(v, u, val);
- }
- }
- void dsu(int u, int pre, bool op) {
- for (int i = head[u]; i; i = edge[i].nxt) {
- int v = edge[i].to;
- if (v == pre || v == son[u])
- continue;
- dsu(v, u, 0);
- }
- if (son[u])
- dsu(son[u], u, 1), curSon = son[u];
- calc(u, pre, 1);
- ans[u] = sum;
- curSon = 0;
- if (!op) {
- mxCnt = sum = 0;
- calc(u, pre, -1);
- }
- }
- int main() {
- read(n);
- for (int i = 1; i <= n; ++i)
- read(color[i]);
- int u, v;
- for (int i = 1; i < n; ++i) {
- read(u), read(v);
- insert(v, u), insert(u, v);
- }
- dfs1(1, 0);
- dsu(1, 0, 1);
- for (int i = 1; i <= n; ++i)
- printf("%I64d ", ans[i]);
- return 0;
- }
【CF600E】Lomsat gelral——树上启发式合并的更多相关文章
- CF600E Lomsat gelral 树上启发式合并
题目描述 有一棵 \(n\) 个结点的以 \(1\) 号结点为根的有根树. 每个结点都有一个颜色,颜色是以编号表示的, \(i\) 号结点的颜色编号为 \(c_i\). 如果一种颜色在以 \(x\) ...
- CF EDU - E. Lomsat gelral 树上启发式合并
学习:http://codeforces.com/blog/entry/44351 E. Lomsat gelral 题意: 给定一个以1为根节点的树,每个节点都有一个颜色,问每个节点的子树中,颜色最 ...
- CF 600 E Lomsat gelral —— 树上启发式合并
题目:http://codeforces.com/contest/600/problem/E 看博客:https://blog.csdn.net/blue_kid/article/details/82 ...
- CF600E Lomsat gelral——线段树合并/dsu on tree
题目描述 一棵树有$n$个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和. 这个题意是真的窒息...具体意思是说,每个节点有一个颜色,你要找的是每个子树中颜色的众数 ...
- Codeforces 600 E. Lomsat gelral (dfs启发式合并map)
题目链接:http://codeforces.com/contest/600/problem/E 给你一棵树,告诉你每个节点的颜色,问你以每个节点为根的子树中出现颜色次数最多的颜色编号和是多少. 最容 ...
- CF600E:Lomsat gelral(线段树合并)
Description 一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和. Input 第一行一个$n$.第二行$n$个数字是$c[i]$.后面$n-1$ ...
- 【学习笔记/题解】树上启发式合并/CF600E Lomsat gelral
题目戳我 \(\text{Solution:}\) 树上启发式合并,是对普通暴力的一种优化. 考虑本题,最暴力的做法显然是暴力统计每一次的子树,为了避免其他子树影响,每次统计完子树都需要清空其信息. ...
- Codeforces 600E - Lomsat gelral(树上启发式合并)
600E - Lomsat gelral 题意 给出一颗以 1 为根的树,每个点有颜色,如果某个子树上某个颜色出现的次数最多,则认为它在这课子树有支配地位,一颗子树上,可能有多个有支配的地位的颜色,对 ...
- [Codeforces600E] Lomsat gelral(树上启发式合并)
[Codeforces600E] Lomsat gelral(树上启发式合并) 题面 给出一棵N个点的树,求其所有子树内出现次数最多的颜色编号和.如果多种颜色出现次数相同,那么编号都要算进答案 N≤1 ...
随机推荐
- Redis---07主从复制(哨兵模式)
一.什么是哨兵模式 基于主从复制的一般模式(一主二从)下,当发生主机发生宕机时,会通过流言协议判断主机是不是宕机,是的话则会通过投票协议自动把某一个从机转换成主机. 二.设置哨兵模式的配置文件 通过r ...
- 关于eslint的使用与配置,以及prettier的使用
eslint官网:https://eslint.bootcss.com/docs/user-guide/getting-started eslint配置:http://eslint.cn/docs/r ...
- src/plugins/Export2Excel.js(目录没有可以重建)
第一步Install cnpm install file-saver 或者 yarn add file-saver cnpm install xlsx 或者 yarn add xlsx cnpm in ...
- 09 Servlet中间服务 连接前段和后端
import 导入 在当前类中使用外包中的类时使用 cookies 缓存 Alt + enter 提示快捷键 Servlet 服务 (连接前段和后端) Servlet本质就是Java类 Ja ...
- sqlsugar入门(2)-C#方法与sugar自定义函数的区别
1.使用tostring获取当天数据 var list = ssc.Queryable<Student>().Where(o => o.CreateTime.Value.ToStri ...
- 快速傅里叶变换学习笔记(FFT)
什么是FFT FFT是用来快速计算两个多项式相乘的一种算法. 如果我们暴力计算两个多项式相乘,复杂度必然是\(O(n^2)\)的,而FFT可以将复杂度降至\(O(nlogn)\) 如何FFT 要学习F ...
- Linux 环境下 C++ 的开发编译
Linux环境下C++程序的开发编译学习笔记 环境:vmware 运行下的Ubuntu 16.04 姓名:谢津 时间:2018/5/24 内容:1)vim的安装及配置:2)第一个C++程序的编写与编译 ...
- ZOJ 1005 Jugs(BFS)
Jugs In the movie "Die Hard 3", Bruce Willis and Samuel L. Jackson were confronted with th ...
- Docker(9)- docker pull 命令详解
如果你还想从头学起 Docker,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1870863.html 作用 从镜像仓库中拉取或更新镜像 ...
- 云计算之路-出海记:整一台 aws 免费云服务器
上次蹭到一张船票,登上了 aws 这艘巨轮,今天要在船上的免费餐厅吃一顿免费晚餐 -- 整一台 aws 免费套餐中的 EC2 服务器体验一下. 进入 EC2 控制台,点击"启动实例" ...