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,表示树节点的数量 ...
随机推荐
- PHP面向对象(OOP)编程入门教程链接
PHP官方学习OOP: http://php.net/manual/zh/oop5.intro.php 从其他博主学习:(以下链接来源: http://blog.snsgou.com/post-41. ...
- Mutual Training for Wannafly Union #2
codeforces 298A. Snow Footprints 分类讨论三种情况: ①..RRRRRR… ②..LLLLLLL… ③..RRRLLLL.. //AC by lwq: #includ ...
- 修改WSAD的默认工作区(转)
如果要一直用某个目录作为工作区,可以在启动WSAD的时候将该目录设为默认工作区,这样启动时直接启动,方便.快捷.但是如果设置后在想换工作区就比较麻烦了,因为没有可供选择的界面了,跟网上搜了一下总结共有 ...
- IntelliJ IDEA自动补全变量名称和属性名称的快捷键
自动补全变量名称 : Ctrl + Alt + v 自动补全属性名称 : Ctrl + Alt + f
- 使用ViewPager实现Tab
一.效果演示及分析 我们直接看两幅图.如下: 上两幅图实现的效果就是: (1)手指左右滑动时,中间的布局呈现不同的效果.而且下面的按钮也会做相应的改变. (2)我们 ...
- 2019.1.3 Mac安装免费版StarUml3.0.2 &&&Xmind思维导图 &&Google浏览器***版
下载StarUml http://staruml.io/ 好像需要FQ 附赠一个 链接:https://pan.baidu.com/s/1_pa9LwopowhOTum5g89zZQ 密码:fxtc ...
- c++ 基础用法、基础知识以及一些常规的操作
1.二叉平衡树:左子树和右子树的深度差小于等于1 2.二叉搜索树:左子树小于根节点的值,右子树大于根结点的值 3.队列:push(),pop(),front(),back(),empty(),size ...
- 金融新手投标模块布局小Demo
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Stack栈 Heap堆
Stack(栈) 栈(stack) 又名堆栈,它是一种运算受限的线性表.其限制是仅允许在表的一端进行插入和删除运算.这一端被称为栈顶,相对地,把另一端称为栈底.向一个栈插入新元素又称作进栈.入栈或压栈 ...
- js获取浏览器上一访问页面URL地址,document.referrer方法
如题,可用document.referrer方法获取上一页面的url 但是也有不可使用的情况 直接在浏览器地址栏中输入地址: 使用location.reload()刷新(location.href或者 ...