传送门

题解

  因为一个sb错误调了一个晚上……鬼晓得我为什么$solve(rt)$会写成$solve(v)$啊!!!一个$O(logn)$被我硬生生写成$O(n)$了竟然还能过$5$个点……话说还一直以为只有动态点分会很难没想到一般点分都这么可啪……%%%大佬

  我们考虑一下,对于一棵树,我们要处理的是子树对根的答案的贡献,以及经过根的路径的贡献(也就是$LCA$为根的点对的答案)。

  对于树中的一个点$i$,如果$i$的颜色是在$i$到根的路径上第一次出现,那么所有与$i$的$LCA$为根的点,都能与$i$的子树形成点对,且$i$的颜色会对那些点产生产生贡献$size[i]$(先不考虑其他子树中是否有相同颜色)。

  那么我们考虑$dfs$一遍整棵树,并记录,所有颜色产生的贡献$col[i]$以及贡献总和$sum$。然后$dfs$子树,并把其中的所有颜色的贡献给减掉。然后考虑子树中的一个点,设$x$为到根上的所有点的贡献总和,$num$表示根到节点上的颜色个数,$y=size[root]-size[该子树]$,那么$ans[j]+=sum-x+num*size[y]$

  然后最后记得加上对根节点的答案的贡献$ans[root]+=sum-col[根的颜色]+size[root]$

  不得不说思路真的挺妙的

 //minamoto
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
char sr[<<],z[];int C=-,Z;
inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
inline void print(ll x){
if(C><<)Ot();if(x<)sr[++C]=,x=-x;
while(z[++Z]=x%+,x/=);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=;
int head[N],Next[N<<],ver[N<<],c[N],son[N],sz[N],size,cnt[N];
ll col[N],ans[N],much,sum,num;
int tot,n,rt;bool vis[N];
inline void add(int u,int v){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
}
void findrt(int u,int fa){
sz[u]=,son[u]=;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!vis[v]&&v!=fa){
findrt(v,u);
sz[u]+=sz[v];
cmax(son[u],sz[v]);
}
}
cmax(son[u],size-sz[u]);
if(son[u]<son[rt]) rt=u;
}
void dfs1(int u,int fa){
//要重新dfs一次,不能直接在找根时的树上做(不然子树和之类的会出错)
//顺便维护各种东西
sz[u]=,++cnt[c[u]];
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!vis[v]&&v!=fa){
dfs1(v,u),sz[u]+=sz[v];
}
}
if(cnt[c[u]]==) sum+=sz[u],col[c[u]]+=sz[u];
--cnt[c[u]];
}
void change(int u,int fa,int val){
++cnt[c[u]];
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!vis[v]&&v!=fa) change(v,u,val);
}
if(cnt[c[u]]==) sum+=sz[u]*val,col[c[u]]+=sz[u]*val;
--cnt[c[u]];
}
void dfs2(int u,int fa){
//把这棵子树里的颜色的影响消除掉
//顺便更新答案
++cnt[c[u]];
if(cnt[c[u]]==) sum-=col[c[u]],++num;
ans[u]+=sum+num*much;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!vis[v]&&v!=fa) dfs2(v,u);
}
if(cnt[c[u]]==) sum+=col[c[u]],--num;
--cnt[c[u]];
}
void clear(int u,int fa){
cnt[c[u]]=col[c[u]]=;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!vis[v]&&v!=fa) clear(v,u);
}
}
void did(int u){
//直接带进去乱搞
dfs1(u,);
ans[u]+=sum-col[c[u]]+sz[u];
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!vis[v]){
//dfs,然后把各种影响消除掉
++cnt[c[u]];
sum-=sz[v];
col[c[u]]-=sz[v];
change(v,u,-);
--cnt[c[u]];
much=sz[u]-sz[v];
dfs2(v,u);
++cnt[c[u]];
sum+=sz[v];
col[c[u]]+=sz[v];
change(v,u,);
--cnt[c[u]];
}
}
sum=,num=,clear(u,);
}
void solve(int u){
did(u),vis[u]=true;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(!vis[v]){
rt=,size=sz[v];
findrt(v,),solve(rt);
}
}
}
int main(){
n=read();
for(int i=;i<=n;++i) c[i]=read();
for(int i=;i<n;++i){
int u=read(),v=read();
add(u,v);
}
son[]=n+,rt=,size=n;
findrt(,),solve(rt);
for(int i=;i<=n;++i) print(ans[i]);
Ot();
return ;
}

洛谷P2664 树上游戏(点分治)的更多相关文章

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

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

  2. 洛谷P2664 树上游戏——点分治

    原题链接 被点分治虐的心态爆炸了 题解 发现直接统计路径上的颜色数量很难,考虑转化一下统计方式.对于某一种颜色\(c\),它对一个点的贡献为从这个点出发且包含这种颜色的路径条数. 于是我们先点分一下, ...

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

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

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

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

  5. ●洛谷P2664 树上游戏

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

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

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

  7. 洛谷P2664 树上游戏

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

  8. P2664 树上游戏

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

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

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

随机推荐

  1. js中获取页面元素节点的几种方式

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  2. gorm中自己写sql的方法实现

    type Result struct { Total int } var result Result //当天修改作业的总时间:分钟 dao.DB(dao.HomeworkTable).Raw(&qu ...

  3. go_结构体和方法

    面向对象:go语言仅支持封装不支持继承和多态 所以go语言没有class,只有struct(结构体) 无论地址还是结构本身,一律用 . 来访问成员 go语言编译器可以自动区分是值传递还是指针传递,值传 ...

  4. 可用于nodejs的SuperAgent(ajax API)

    简单示例: import request from 'superagent';//引用声明 request.post(api) .withCredentials()//跨域 .end((err, re ...

  5. Cookie、Session、Token

    一.发展史 .最初.Web基本上就是文档的浏览而已,既然是浏览,作为服务器,不需要记录谁在某一段时间里都浏览了什么文档,每次请求都是一个新的HTTP协议,就是请求加相应,尤其是我不用记住是谁刚刚发了H ...

  6. web图形方案比较html5、GML、SVG、VML

    GML.SVG和VML都是基于XML的可用来描述矢量图形的标记语言,都是XML词表,它们的语法并不难理解,但它们都有各自不同的用途和特点,下面简单介绍一下. GML(Geography Markup  ...

  7. 如果使用mybatis的逆向工程生成的po类及mapper,如果我们想要进行的对数据库的操作在mapper中没有对应的接口函数:比如生成的mapper接口中没有按照姓名及性别混合条件查询。我们的解决办法是:使用逆向工程生成的对应表的Example文件。

    1.使用mybatis逆向工程生成的po类中包含UserExample文件(我的数据库表名为User). 2. 创建UserExample对象,然后对加入条件.对应的测试代码为: /* * 通过姓名和 ...

  8. 安装bcmath 扩展

    1.在php源码包中,默认就包含bcmath扩展的安装文件,只需手动安装一下即可 cd /root/build2/php-/ext/bcmath // 进入PHP的源码包目录中的bcmatch扩展目录 ...

  9. button作用类似于submit

    不想提交,可使用以下 <a href="javascript:;" >修改</a>

  10. 玩转Sketch,不容错过的5大实用插件推荐

    在之前的文章中,笔者为大家介绍了Sketch 的入门教程.实用技巧和资源集锦,相信大家对Sketch已经有了初步的了解和认识.除了基础的矢量设计功能以外,插件更是让Sketch保持强大的独门秘籍.Sk ...