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 树上游戏的更多相关文章

  1. 洛谷 P2664 树上游戏 解题报告

    P2664 树上游戏 题目描述 \(\text{lrb}\)有一棵树,树的每个节点有个颜色.给一个长度为\(n\)的颜色序列,定义\(s(i,j)\) 为 \(i\) 到 \(j\) 的颜色数量.以及 ...

  2. Luogu P2664 树上游戏 dfs+树上统计

    题目: P2664 树上游戏 分析: 本来是练习点分治的时候看到了这道题.无意中发现题解中有一种方法可以O(N)解决这道题,就去膜拜了一下. 这个方法是,假如对于某一种颜色,将所有这种颜色的点全部删去 ...

  3. ●洛谷P2664 树上游戏

    题链: https://www.luogu.org/problemnew/show/P2664题解: 扫描线,线段树维护区间覆盖 https://www.luogu.org/blog/ZJ75211/ ...

  4. 洛谷P2664 树上游戏(点分治)

    传送门 题解 因为一个sb错误调了一个晚上……鬼晓得我为什么$solve(rt)$会写成$solve(v)$啊!!!一个$O(logn)$被我硬生生写成$O(n)$了竟然还能过$5$个点……话说还一直 ...

  5. 洛谷P2664 树上游戏

    https://www.luogu.org/problemnew/show/P2664 #include<cstdio> #include<algorithm> #includ ...

  6. [LuoGu]P2664 树上游戏

    Portal 这题真的好. 看到树上路径, 脑子里就要点分治 这一题对于每个点都要计算一遍, 如果暴算实在不好算, 这样我们就可以考虑算贡献. 直接计算每种颜色的贡献. 因为一条过重心的路径中, 可能 ...

  7. 洛谷P2664 树上游戏(点分治)

    题意 题目链接 Sol 神仙题..Orz yyb 考虑点分治,那么每次我们只需要统计以当前点为\(LCA\)的点对之间的贡献以及\(LCA\)到所有点的贡献. 一个很神仙的思路是,对于任意两个点对的路 ...

  8. 【刷题】洛谷 P2664 树上游戏

    题目描述 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 \[sum_i=\sum_{j=1}^ns(i,j)\] 现在他想让你求出所有 ...

  9. 洛谷P2664 树上游戏 【点分治 + 差分】

    题目 lrb有一棵树,树的每个节点有个颜色.给一个长度为n的颜色序列,定义s(i,j) 为i 到j 的颜色数量.以及 现在他想让你求出所有的sum[i] 输入格式 第一行为一个整数n,表示树节点的数量 ...

随机推荐

  1. mongodb 创建auto increment 自增函数

    计数器表  > db.counters.insert({_id: "userid",seq: 0}  ); > db.counters.find();  { " ...

  2. Java导出Highcharts需要的3个外部jar包

    xerces batik fop 这三个JAR包. 绝对可用.自本用过. 如果两个项目在同一个TOMCAT下并且同时用到xerces.jar,需要前这个外放在TOMCAT下的lib目录下.其他的容器中 ...

  3. Using the @synchronized Directive

    https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/ThreadSafe ...

  4. 十一.安装Redis

    redis是一个NOSql数据库,在java web中,经常用来存储session等,官网是https://redis.io/ 当前官网指出的稳定版本为3.2.6,下载地址为http://downlo ...

  5. [USACO07OPEN]吃饭Dining

    嘟嘟嘟 这应该是网络流入门题之一了,跟教辅的组成这道题很像. 把每一只牛看成书,然后对牛拆点,因为每一只牛只要一份,食物和饮料分别看成练习册和答案. #include<cstdio> #i ...

  6. python之self

    python中的self与Java中的this类似,类的函数通过self引用从而实现对类的数据类型进行访问操作. 1. self在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数.(类的方法与 ...

  7. Linux脚本开头#!/bin/bash和#!/bin/sh是什么意思以及区别

    一.意思 #!/bin/sh是指此脚本使用/bin/sh来解释执行,#!是特殊的表示符,其后面根的是此解释此脚本的shell的路径. 其实第一句的#!是对脚本的解释器程序路径,脚本的内容是由解释器解释 ...

  8. 用Java+xml配置方式实现Spring数据事务(编程式事务)

    一.用Java配置的方式 1.实体类: Role public class Role { private int id; private String roleName; private String ...

  9. Spring通过注解装配Bean

    通过注解实现ServiceImpl业务 一.使用@Component装配Bean 1. 定义类:User 在类上面加@Component注解,在属性上面加@Value值 package com.wbg ...

  10. EF Core 2.0中Transaction事务会对DbContext底层创建和关闭数据库连接的行为有所影响

    数据库 我们先在SQL Server数据库中建立一个Book表: CREATE TABLE [dbo].[Book]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Cr ...