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,表示树节点的数量 ...
随机推荐
- 用python实现矩阵转置
前几天群里有同学提出了一个问题:手头现在有个列表,列表里面两个元素,比如[1, 2],之后不断的添加新的列表,往原来相应位置添加.例如添加[3, 4]使原列表扩充为[[1, 3], [2, 4]],再 ...
- 显卡 GPU 关系
https://zhidao.baidu.com/question/1238935513507031339.htmlGraphic Processing Unit,意思就是图形处理器啊,显卡的由GPU ...
- miniui dataGrid drawcell事件
var grid = mini.get("tpaTotal2_grid"); //grid.load(); //单元格绘制事件 grid. ...
- PHP----练习-----三级联动
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- MyBatis(9)整合spring
具体的感兴趣可以参考:MyBatis 此时此刻,没用的话不再多说了,直接开始代码工程吧! 整体的代码实现: 具体使用到的我们在进行细说 基本上理解一边就能会使用整合 准备工作: db.proper ...
- 【luogu P2279 [HNOI2003]消防局的设立】 题解
题目链接:https://www.luogu.org/problemnew/show/P2279 想怎么贪怎么贪 #include <queue> #include <cstdio& ...
- [转]asp.net Request、Response 响应在此上下文中不可用
这个问题一般是在页面中使用了Respons.Write()方法,虽然是继承了System.Web.UI.Page.但System.Web.UI.Page下的Response只有在asp.net页面里才 ...
- [转] 各种Json解析工具比较 - json-lib/Jackson/Gson/FastJson
JSON技术的调研报告 一 .各个JSON技术的简介和优劣1.json-libjson-lib最开始的也是应用最广泛的json解析工具,json-lib 不好的地方确实是依赖于很多第三方包,包括com ...
- HDU 2092 (将表达式变成一元二次方程形式)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=2092 整数解 Time Limit: 1000/1000 MS (Java/Others) Me ...
- JQuery Ajax 使用FormData上传文件对象
FormData部分: 先new FormData对象 :let somedata = new FormData(),然后将数据添加进去,这里我们使用append()进行添加. 这里举一个上传头像的例 ...