[LuoGu]P2664 树上游戏
这题真的好.
看到树上路径, 脑子里就要点分治
这一题对于每个点都要计算一遍, 如果暴算实在不好算, 这样我们就可以考虑算贡献.
直接计算每种颜色的贡献.
因为一条过重心的路径中, 可能两边都会有相同颜色, 那么我们就只考虑当前点到分治中心的链上.
然后把多算的这条链上的东西删除就可以了.
Copy From Here
那么我们就可以这样做了:
1.对树进行第一遍dfs,预处理size和上方性质中的贡献。(开一个color数组即可记录贡献),同时记录贡献总和sum
2.枚举跟的所有子树,先把子树扫一遍清除其在color数组中的所有贡献。接着,对于该子树中的每一个点j:
设X=sigma color[j 到根上(不包括根)的所有颜色] 由于这些颜色已经出现过,我们不能在该子树外计算其贡献)
设num为j到根上(不包括根)的颜色数
设Y为size[root]-size[该子树](即所有其他子树+根的点数)
则ans[j]+=sum-X+num*Y;
3.别忘了计算单独root的ans
ans[root]+=sum-color[根的颜色]+size[root]
4.清空贡献数组以及其他东西
Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
char ch = getchar();
int x = 0, flag = 1;
for (;!isdigit(ch); ch = getchar()) if (ch == '-') flag *= -1;
for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
return x * flag;
}
void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x >= 10) write(x / 10);
putchar(x % 10 + 48);
}
const int Maxn = 100009;
struct edge {
int to, nxt;
}g[Maxn << 1];
int n, a[Maxn], head[Maxn], e;
void add(int u, int v) {
g[++e] = (edge){v, head[u]}, head[u] = e;
}
void init() {
clar(head, -1);
n = read();
rep (i, 1, n) a[i] = read();
rep (i, 1, n - 1) {
int u = read(), v = read();
add(u, v), add(v, u);
}
}
LL ans[Maxn], vis[Maxn], Tmp, heart;
int size[Maxn];
void dfs(int u, int pa) {
size[u] = 1;
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != pa && !vis[v]) {
dfs(v, u);
size[u] += size[v];
}
}
}
void centroid(int u, int pa, int field) {
int bbs = field - size[u];
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (!vis[v] && v != pa) {
centroid(v, u, field);
bbs = max(bbs, size[v]);
}
}
if (bbs < Tmp) Tmp = bbs, heart = u;
}
LL color[Maxn], Sum, cnt[Maxn], size1[Maxn];
void dfs_init(int u, int pa) {
size1[u] = 1; ++cnt[a[u]];
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != pa && !vis[v]) {
dfs_init(v, u);
size1[u] += size1[v];
}
}
if (cnt[a[u]] == 1) {
color[a[u]] += size1[u];
Sum += size1[u];
}
--cnt[a[u]];
}
void change(int u, int pa, int delta) {
++cnt[a[u]];
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != pa && !vis[v]) change(v, u, delta);
}
if (cnt[a[u]] == 1) {
color[a[u]] += size1[u] * 1ll * delta;
Sum += size1[u] * 1ll * delta;
}
--cnt[a[u]];
}
void count(int u, int pa, int X, int Y, int num) {
++cnt[a[u]];
if (cnt[a[u]] == 1) {
X += color[a[u]];
++Y;
}
ans[u] += Sum - X + num * Y;
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (!vis[v] && v != pa) count(v, u, X, Y, num);
}
--cnt[a[u]];
}
void dfs_clear(int u, int pa) {
color[a[u]] = 0, cnt[a[u]] = 0, size1[u] = 0;
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (v != pa && !vis[v]) dfs_clear(v, u);
}
}
void calc(int u) {
dfs_init(u, 0);
ans[u] += Sum - color[a[u]] + size1[u];
for (int i = head[u]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (!vis[v]) {
++cnt[a[u]];
Sum -= size1[v]; color[a[u]] -= size1[v];
change(v, u, -1);
--cnt[a[u]];
count(v, u, 0, 0, size1[u] - size1[v]);
++cnt[a[u]];
Sum += size1[v]; color[a[u]] += size1[v];
change(v, u, 1);
--cnt[a[u]];
}
}
dfs_clear(u, 0), Sum = 0;
}
void divConquer(int u) {
dfs(u, 0), Tmp = INT_MAX;
centroid(u, 0, size[u]);
calc(heart); vis[heart] = 1;
for (int i = head[heart]; ~i; i = g[i].nxt) {
int v = g[i].to;
if (!vis[v]) divConquer(v);
}
}
void solve() {
divConquer(1);
rep (i, 1, n) printf("%lld\n", ans[i]);
}
int main() {
freopen("LG2664.in", "r", stdin);
freopen("LG2664.out", "w", stdout);
init();
solve();
#ifdef Qrsikno
debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
return 0;
}
[LuoGu]P2664 树上游戏的更多相关文章
- Luogu P2664 树上游戏 dfs+树上统计
题目: P2664 树上游戏 分析: 本来是练习点分治的时候看到了这道题.无意中发现题解中有一种方法可以O(N)解决这道题,就去膜拜了一下. 这个方法是,假如对于某一种颜色,将所有这种颜色的点全部删去 ...
- luogu P2664 树上游戏(点分治)
点分治真是一个好东西.可惜我不会 这种要求所有路经的题很可能是点分治. 然后我就不会了.. 既然要用点分治,就想,点分治有哪些优点?它可以\(O(nlogn)\)遍历分治树的所有子树. 那么现在的问题 ...
- P2664 树上游戏
P2664 树上游戏 https://www.luogu.org/problemnew/show/P2664 分析: 点分治. 首先关于答案的统计转化成计算每个颜色的贡献. 1.计算从根出发的路径的答 ...
- 洛谷 P2664 树上游戏 解题报告
P2664 树上游戏 题目描述 \(\text{lrb}\)有一棵树,树的每个节点有个颜色.给一个长度为\(n\)的颜色序列,定义\(s(i,j)\) 为 \(i\) 到 \(j\) 的颜色数量.以及 ...
- ●洛谷P2664 树上游戏
题链: https://www.luogu.org/problemnew/show/P2664题解: 扫描线,线段树维护区间覆盖 https://www.luogu.org/blog/ZJ75211/ ...
- 洛谷P2664 树上游戏
https://www.luogu.org/problemnew/show/P2664 #include<cstdio> #include<algorithm> #includ ...
- 洛谷P2664 树上游戏(点分治)
传送门 题解 因为一个sb错误调了一个晚上……鬼晓得我为什么$solve(rt)$会写成$solve(v)$啊!!!一个$O(logn)$被我硬生生写成$O(n)$了竟然还能过$5$个点……话说还一直 ...
- 洛谷P2664 树上游戏(点分治)
题意 题目链接 Sol 神仙题..Orz yyb 考虑点分治,那么每次我们只需要统计以当前点为\(LCA\)的点对之间的贡献以及\(LCA\)到所有点的贡献. 一个很神仙的思路是,对于任意两个点对的路 ...
- luogu P2644 树上游戏
一道点分难题 首先很自然的想法就是每种颜色的贡献可以分开计算,然后如果你会虚树就可以直接做了 点分也差不多,考虑每个分治重心的子树对它的贡献以及它对它子树的贡献 首先,处理一个\(cnt\)数组,\( ...
随机推荐
- ubuntu搭建samba服务器
一.为什么要用Samba? Samba的主要任务就是实现Linux系统和Windows系统之间的资源共享. 二.需要的软件? 我是在ubuntu上实现的,所以我只需在配置好ubuntu的更 ...
- linux 文件记录锁详解
一: linux记录锁更恰当的称呼应该是范围锁,它是对文件某个范围的锁定. 关于记录锁的功能就是fcntl提供的第五个功能,具体使用如下: int fcntl(int fd, int cmd, str ...
- 三张图教你生成一个Android jar 库。
我看到非常多教人使用第三方开源组件的Android教程.都是在教基于源代码project的库导入,个人觉得非常不妥,觉得最恰当的方式是把源代码project生成一个jar再导入到目标project上使 ...
- bash shell和进程
1 exec builtin 不创建子shell,在原进程的上启动新的脚本,但是它会把老shell的环境清理掉,所以,它从原shell中什么也不继承,在一个干净的环境中执行新的脚本.执行完之后退出当前 ...
- 倒排索引 获取指定单词的文档集合 使用hash去重单词term 提高数据压缩率的方法
倒排索引源于实际应用中需要根据属性的值来查找记录.这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址.由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inve ...
- socketIO原理图
- 使用URL dispatcher的范例
在上面的一篇文章中,我们介绍了怎样使用URL disptacher.在这篇文章中,我们来通过一个范例更进一步来了解怎样实现它. 1)创建一个具有URL dispatcher的应用 我们首先打开我们的S ...
- nrm -- NPM registry 管理工具(附带测速功能)
在使用npm时,官方的源下载npm包会比较慢,国内我们基本使用淘宝的源.nrm 是一个 NPM 源管理器,可以允许你快速地在 NPM 源间切换. Install npm install -g nrm ...
- ZOJ3209 Treasure Map —— Danc Links 精确覆盖
题目链接:https://vjudge.net/problem/ZOJ-3209 Treasure Map Time Limit: 2 Seconds Memory Limit: 32768 ...
- AutoEventWireup
Page_PreInit & OnPreInit - whats the difference? https://forums.asp.net/t/1095903.aspx?Page_PreI ...