P2664 树上游戏
P2664 树上游戏
https://www.luogu.org/problemnew/show/P2664
分析:
点分治。
首先关于答案的统计转化成计算每个颜色的贡献。
1、计算从根出发的路径的答案:如果某一个颜色是从根到这个点的链上的第一次出现的,那么这个颜色会对根产生siz[x]个贡献。(根连向它子树的任意一个点的路径都包含这个颜色)。
2、计算子树内每个点过根的路径答案:记录一个数组sum[i],表示从根出发包含颜色i的路径的条数(在1中,找到一个第一次出现的颜色,加上这个点的siz即可)。然后假设当前点是x,根为z,x所在的子树为y。x->z的路径上,出现的颜色为Num,那么这Num个颜色由于已经在到根的路径上有了,那么随便选择其它子树内的点构成的路径都包含了这个颜色,贡献为(siz[z]-siz[y])*Num;未出现的颜色的贡献:在y子树外计算多少个点与x构成的路径,包含这个颜色。那么可以sum数组的和得到,但是其中包含的y子树的路径,所以一开始要先减掉。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
using namespace std;
typedef long long LL; inline int read() {
int x=,f=;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-;
for(;isdigit(ch);ch=getchar())x=x*+ch-'';return x*f;
} const int INF = 1e9;
const int N = ; int head[N], nxt[N << ], to[N << ], En;
int col[N], siz[N], sk[N];
LL ans[N], cnt[N], sum[N], Sum, Num;
bool vis[N];
int Size, Mn, Root, top;
// cnt[i] 颜色i出现的次数,sum[i]以根为起点,包含颜色i的路径条数,Sum为sum[i]的和。 void add_edge(int u,int v) {
++En; to[En] = v; nxt[En] = head[u]; head[u] = En;
++En; to[En] = u; nxt[En] = head[v]; head[v] = En;
} void getroot(int u,int fa) {
siz[u] = ;
int cnt = ;
for (int i=head[u]; i; i=nxt[i]) {
int v = to[i];
if (v == fa || vis[v]) continue; // vis[v]!!!
getroot(v, u);
siz[u] += siz[v];
cnt = max(cnt, siz[v]);
}
cnt = max(cnt, Size - siz[u]);
if (cnt < Mn) { Mn = cnt, Root = u; }
} void dfs1(int u,int fa) { // 计算siz,sum,以根为起点的答案。
siz[u] = ; cnt[col[u]] ++;
for (int i=head[u]; i; i=nxt[i])
if (!vis[to[i]] && to[i] != fa)
dfs1(to[i], u), siz[u] += siz[to[i]];
if (cnt[col[u]] == )
Sum += siz[u], sum[col[u]] += siz[u], sk[++top] = col[u];
cnt[col[u]] --;
} void dfs2(int u,int fa) { // 计算子树内每个点 过根的所有路径的答案。
cnt[col[u]] ++;
if (cnt[col[u]] == )
Num ++, Sum -= sum[col[u]]; // 只考虑过根的路径,Num记录这个点到根的路径第一次出现的颜色的个数
ans[u] += Num * Size + Sum;
// 这些Num个颜色因为到根的路径上已经有这个颜色了,所以和其他的点任意组合的路径都满足有这个颜色;Sum为除了Num个颜色以外的颜色的贡献
for (int i=head[u]; i; i=nxt[i])
if (!vis[to[i]] && to[i] != fa) dfs2(to[i], u);
if (cnt[col[u]] == )
Num --, Sum += sum[col[u]];
cnt[col[u]] --;
} void Modify(int u,int fa,int p) {
cnt[col[u]] ++;
for (int i=head[u]; i; i=nxt[i])
if (!vis[to[i]] && to[i] != fa) Modify(to[i], u, p);
if (cnt[col[u]] == )
Sum += siz[u] * p, sum[col[u]] += siz[u] * p;
cnt[col[u]] --;
}
void change(int u,int fa,int p) {
Sum += siz[u] * p, sum[col[fa]] += siz[u] * p; // sum[col[fa]]=siz[fa],现在应该减去这棵子树
cnt[col[fa]] ++; Modify(u, fa, p); cnt[col[fa]] --;
} void Calc(int u) {
top = ; dfs1(u, ); ans[u] += Sum; // 计算子树内每个点的siz,sum,以根为起点的答案。
for (int i=head[u]; i; i=nxt[i]) {
int v = to[i];
if (vis[v]) continue;
change(v, u, -); // 把这棵子树的贡献减去
Size = siz[u] - siz[v]; dfs2(v, u); // Size = siz[v] !!!,计算子树内的每个点答案。
change(v, u, ); // 加回来
}
Num = Sum = ;
for (int i=; i<=top; ++i) cnt[sk[i]] = sum[sk[i]] = ;
}
void solve(int u) {
Calc(u); vis[u] = true;
for (int i=head[u]; i; i=nxt[i]) {
int v = to[i];
if (vis[v]) continue;
Size = siz[v], Mn = INF;
getroot(v, );
solve(Root);
}
} int main() {
int n = read();
for (int i=; i<=n; ++i) col[i] = read();
for (int i=; i<n; ++i) {
int x = read(), y = read();
add_edge(x, y);
}
Size = n, Mn = 1e9;
getroot(, );
solve(Root);
for (int i=; i<=n; ++i) printf("%lld\n",ans[i]);
return ;
}
P2664 树上游戏的更多相关文章
- 洛谷 P2664 树上游戏 解题报告
P2664 树上游戏 题目描述 \(\text{lrb}\)有一棵树,树的每个节点有个颜色.给一个长度为\(n\)的颜色序列,定义\(s(i,j)\) 为 \(i\) 到 \(j\) 的颜色数量.以及 ...
- Luogu P2664 树上游戏 dfs+树上统计
题目: P2664 树上游戏 分析: 本来是练习点分治的时候看到了这道题.无意中发现题解中有一种方法可以O(N)解决这道题,就去膜拜了一下. 这个方法是,假如对于某一种颜色,将所有这种颜色的点全部删去 ...
- ●洛谷P2664 树上游戏
题链: https://www.luogu.org/problemnew/show/P2664题解: 扫描线,线段树维护区间覆盖 https://www.luogu.org/blog/ZJ75211/ ...
- 洛谷P2664 树上游戏(点分治)
传送门 题解 因为一个sb错误调了一个晚上……鬼晓得我为什么$solve(rt)$会写成$solve(v)$啊!!!一个$O(logn)$被我硬生生写成$O(n)$了竟然还能过$5$个点……话说还一直 ...
- 洛谷P2664 树上游戏
https://www.luogu.org/problemnew/show/P2664 #include<cstdio> #include<algorithm> #includ ...
- [LuoGu]P2664 树上游戏
Portal 这题真的好. 看到树上路径, 脑子里就要点分治 这一题对于每个点都要计算一遍, 如果暴算实在不好算, 这样我们就可以考虑算贡献. 直接计算每种颜色的贡献. 因为一条过重心的路径中, 可能 ...
- 洛谷P2664 树上游戏(点分治)
题意 题目链接 Sol 神仙题..Orz yyb 考虑点分治,那么每次我们只需要统计以当前点为\(LCA\)的点对之间的贡献以及\(LCA\)到所有点的贡献. 一个很神仙的思路是,对于任意两个点对的路 ...
- 【刷题】洛谷 P2664 树上游戏
题目描述 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 \[sum_i=\sum_{j=1}^ns(i,j)\] 现在他想让你求出所有 ...
- 洛谷P2664 树上游戏 【点分治 + 差分】
题目 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 现在他想让你求出所有的sum[i] 输入格式 第一行为一个整数n,表示树节点的数量 ...
随机推荐
- 自动生成气泡对话框的jQuery插件CreateBubble.js
之前在写一个界面,想要用到气泡,然而一直找不到现成的有效地办法,是在没有办法了我只好自己写一个,于是就有了现在的CreateBubble.js.很简单的一个函数,但是非常实用. 使用方法: 1.HTM ...
- Jenkins传参修改jmeter的报告名称和详细报告地址
目前已经可以发送邮件了,我已经配置了Jenkins,但是有几个显示问题,待处理1.报告名称地址, 2详细报告地址没有做跳转 更改后为: 修改url 1.打开样式的jmeter-results-deta ...
- 异常:Neither BindingResult nor plain target object for bean name 'command' available as request attribute
Neither BindingResult nor plain target object for bean name 'command' available as request attribute ...
- 「hihocoder1413 Rikka with String」
题目 哈哈哈哈哈哈哈哈哈哈我还没自闭 好像前后调了两天了 哈哈哈哈哈哈哈哈哈哈我还没自闭 这道题就是给定一个小写字母串,回答分别把每个位置上的字符替换为\(#\)后的本质不同的子串数 首先就是跨过这个 ...
- 20145223 杨梦云 《网络对抗》 Web基础
20145223 杨梦云 <网络对抗> Web基础 1.实验后回答问题 (1)什么是表单 表单在网页中主要负责数据采集功能.一个表单有三个基本组成部分:(1) 表单标签:这里面包含了处理表 ...
- MVC验证码生成类库
public class ValidateCode { /// <summary> /// 验证码的最大长度 /// </summary> public int MaxLeng ...
- enmu枚举类型
在实际问题中,有些变量的取值被限定在一个有限的范围内.例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等.如果把这些量说明为整型,字符型或其它类型显然是不妥当的.为此,C语言提供了一 ...
- win8开发
http://msdn.microsoft.com/library/default.aspx
- 大数据框架-Hbase
大规模结构化集群存储数据库.Table中的所有行都按照row key的字典序排列. 主键:row Key.访问行只能通过rowKey访问(范围或者准确值),或者全表扫描: 列族:cloumn fami ...
- 谨慎修改Oracle数据库字符集(UTF8<->ZHS16GBK)
Preface Today,I'm gonna say something what is related with the character set in Oracle datab ...