题面戳我

sol

点分。我们面临的最主要一个问题,就是如何在\(O(n)\)的时间内算出所有LCA为根的点对的贡献,还要分别累加到它们自己的答案中去。

\(num_i\):每一种颜色的数量。你可以认为这就是一个桶。从根到叶子遍历,相当于每次都只维护一条链上的颜色情况。以便于得到\(tot_i\)

\(fst_i\):\(i\)号点上的颜色是不是从根往下第一次出现。如果是,就会加到\(col_i\)里面取算贡献

\(col_i\):每一种颜色的贡献

\(tot_i\):每个点到根的路径上有多少种颜色

鉴于点对之间计算答案不太现实,我们考虑计算每种颜色对答案的贡献。

如果一个节点\(i\),它的颜色是从根往下第一次出现的(即\(fst_i=1\)),那么这种颜色就一定会给其他子树中的每个节点贡献\(sz_i\)的答案。这个答案就累加在\(col_i\)中。然后在对这个\(col_i\)求和,就是总贡献。

一个节点\(i\)的答案的初始值应该是\(tot_i*(sz_u-sz_v)\)(就是总\(sz\)除去自己所在的子树外的部分),然后还要加上一些\(col_i\)的值,但是要保证加上的\(col_i\)不能是自己到根已经有过的颜色(不然就重复计算了)。

多做几遍dfs,维护以上提到的东西就行了。

复杂度是\(O(nlog_2n)\)的,带点小常数

code

我之前干了件非常傻逼的事情

我没写\(clear\)在\(solve\)函数里面写了个memset

然后复杂度变成了严格\(O(n^2)\)

然后就只有暴力分。。。

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 100005;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
struct edge{int to,next;}a[N<<1];
int n,c[N],head[N],cnt,sz[N],w[N],vis[N],sum,root;
int num[N],fst[N],col[N],tot[N];
ll sigma,ans[N];
void getroot(int u,int f)
{
sz[u]=1;w[u]=0;
for (int e=head[u];e;e=a[e].next)
{
int v=a[e].to;if (v==f||vis[v]) continue;
getroot(v,u);
sz[u]+=sz[v];w[u]=max(w[u],sz[v]);
}
w[u]=max(w[u],sum-sz[u]);
if (w[u]<w[root]) root=u;
}
void dfs(int u,int f,ll &Ans)
{
sz[u]=1;num[c[u]]++;
if (num[c[u]]==1) fst[u]=1,cnt++;else fst[u]=0;
tot[u]=cnt;Ans+=tot[u];
for (int e=head[u];e;e=a[e].next)
{
int v=a[e].to;if (v==f||vis[v]) continue;
dfs(v,u,Ans);sz[u]+=sz[v];
}
if (fst[u]) col[c[u]]+=sz[u],sigma+=sz[u],cnt--;
num[c[u]]--;
}
void change(int u,int f,int b)
{
if (fst[u]) col[c[u]]+=b*sz[u],sigma+=b*sz[u];
for (int e=head[u];e;e=a[e].next)
{
int v=a[e].to;if (v==f||vis[v]) continue;
change(v,u,b);
}
}
void calc(int u,int f,int k)
{
if (fst[u]) sigma-=col[c[u]];
ans[u]+=1ll*tot[u]*k+sigma;
for (int e=head[u];e;e=a[e].next)
{
int v=a[e].to;if (v==f||vis[v]) continue;
calc(v,u,k);
}
if (fst[u]) sigma+=col[c[u]];
}
void clear(int u,int f)
{
col[c[u]]=0;
for (int e=head[u];e;e=a[e].next)
{
int v=a[e].to;if (v==f||vis[v]) continue;
clear(v,u);
}
}
void solve(int u)
{
vis[u]=1;
dfs(u,0,ans[u]);
col[c[u]]-=sz[u];sigma-=sz[u];
for (int e=head[u];e;e=a[e].next)
{
int v=a[e].to;if (vis[v]) continue;
change(v,0,-1);
calc(v,0,sz[u]-sz[v]);
change(v,0,1);
}
clear(u,0);sigma=0;
for (int e=head[u];e;e=a[e].next)
{
int v=a[e].to;if (vis[v]) continue;
sum=sz[v];
root=0;
getroot(v,0);
solve(root);
}
}
int main()
{
n=gi();
for (int i=1;i<=n;i++) c[i]=gi();
for (int i=1;i<n;i++)
{
int u=gi(),v=gi();
a[++cnt]=(edge){v,head[u]};head[u]=cnt;
a[++cnt]=(edge){u,head[v]};head[v]=cnt;
}
sum=w[0]=n;cnt=0;
getroot(1,0);
solve(root);
for (int i=1;i<=n;i++) printf("%lld\n",ans[i]);
return 0;
}

[Luogu2664]树上游戏的更多相关文章

  1. 【Luogu2664】树上游戏(点分治)

    [Luogu2664]树上游戏(点分治) 题面 洛谷 题解 很好的一道点分治题. 首先直接点分治,考虑过每个分治重心的链的贡献. 我们从分治重心开始找每种颜色,强制令一种颜色只在其到分治重心的链上第一 ...

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

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

  3. P2664 树上游戏

    P2664 树上游戏 https://www.luogu.org/problemnew/show/P2664 分析: 点分治. 首先关于答案的统计转化成计算每个颜色的贡献. 1.计算从根出发的路径的答 ...

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

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

  5. LG2664 树上游戏

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

  6. 【Luogu P2664】树上游戏

    Problem Description \(lrb\) 有一棵树,树的每个节点有个颜色.给一个长度为 \(n\) 的颜色序列,定义 \(s(i,j)\) 为 \(i\) 到 \(j\) 的颜色数量.以 ...

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

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

  8. [LuoGu]P2664 树上游戏

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

  9. luoguP2664树上游戏(点分治)

    题目链接:https://www.luogu.org/problem/P2664 题意:给定一颗带点权的树,结点数n<=1e5,点权<=1e5,用s(i,j)表示从i到j的路径上不同点权数 ...

随机推荐

  1. python入门学习笔记(二)

    6.6替换元素 7.tuple类型 7.1创建tuple 7.2创建单元素tuple    7.3"可变"的tuple 8.条件判断和循环 8.1,if语句   8.2,if... ...

  2. 浅谈扩展欧几里得算法(exgcd)

    在讲解扩展欧几里得之前我们先回顾下辗转相除法: \(gcd(a,b)=gcd(b,a\%b)\)当a%b==0的时候b即为所求最大公约数 好了切入正题: 简单地来说exgcd函数求解的是\(ax+by ...

  3. [UWP]做个调皮的BusyIndicator

    1. 前言 最近突然想要个BusyIndicator.做过WPF开发的程序员对BusyIndicator应该不陌生,Extended WPF Toolkit 提供了BusyIndicator的开源实现 ...

  4. PHP 支持加解密的函数

    function encrypt($string,$operation,$key=''){ $key=md5($key); $key_length=strlen($key); $string=$ope ...

  5. PHP操作Redis队列的方法

    入队: <?php $redis = new Redis(); $redis->connect('127.0.0.1',6379); while(True){ try{ $value = ...

  6. DxPackNet 2.视频截图和捕捉帧图片

    在上一节的基础上 打开了摄像头后: 1.视频截图------调用  CatchBmp 方法即可获取当前帧的 bmp 图像, //调用截屏函数 获取当前图片 Bitmap bmp = camCaptur ...

  7. uva140

    全排列回溯剪枝. 题目数据很水.记录当前最小带宽,边回溯边计算当前序列最大的距离(也就是带宽),如果当前带宽超过了当前的最小带宽就剪枝. 注意下,数据读入时的字符串处理. AC代码 #include& ...

  8. vue之render基本书写方法

    Vue 推荐在绝大多数情况下使用 template 来创建你的 HTML.然而在一些场景中,你真的需要 JavaScript 的完全编程的能力,这就是 render 函数,它比 template 更接 ...

  9. Android的微信智能心跳方案

    原文地址: 年11月中旬时,因为基础组件组人手紧张,Leo安排我和春哥去广州轮岗支援.刚到广州的时候,Ray让我和春哥对Line和WhatsApp的心跳机制进行分析.我和春哥抓包测试了差不多两个多礼拜 ...

  10. ARM架构

    ARM架构(过去称作进阶精简指令集机器(Advanced RISC Machine),更早称作Acorn RISC Machine)是一个32位元精简指令集(RISC) 中央处理器(processor ...