点分治真是一个好东西。可惜我不会

这种要求所有路经的题很可能是点分治。

然后我就不会了。。

既然要用点分治,就想,点分治有哪些优点?它可以\(O(nlogn)\)遍历分治树的所有子树。

那么现在的问题就是,如可快速(\(O(n)\)或O\((nlogn)\))求以一个点为根的时候,子树之间的贡献(当然还有根节点的)。

我们注意到一件事,就是一棵子树中一个点对其他子树的点产生贡献当且仅当这个点的颜色在它到根的路径上第一次出现(或者说只算上这些贡献答案正确),且贡献为以这个点为根的子树大小。(不考虑其它子树的颜色)

这个有什么用,我们可以遍历两遍子树,第一遍预处理出所有子树对其它子树的贡献(如上边一段所说把贡献统计),第二次遍历每一颗子树先把这颗树的贡献去掉,统计所有其它的树对这颗树的贡献。

那么具体该怎么做?

void calc(int u){
dfs1(u,0);
ans[u]+=sum;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v])continue;
cnt[a[u]]++;
sum-=size[v];color[a[u]]-=size[v];
change(v,u,-1);
cnt[a[u]]--;
tot=size[u]-size[v];
dfs2(v,u);
cnt[a[u]]++;
sum+=size[v];color[a[u]]+=size[v];
change(v,u,1);
cnt[a[u]]--;
}
clear(u,0);
}

首先dfs1是统计贡献的,用sum记录贡献和,color[i]记录第i种颜色的贡献。

然后根的答案就可以累加了。

那么如可判断一个颜色第一次出现?可以记录一个cnt[i]记录第i种颜色在到根的路径上出现多少次。当cnt[i]等于1的时候统计贡献。

然后

		cnt[a[u]]++;
sum-=size[v];color[a[u]]-=size[v];
change(v,u,-1);
cnt[a[u]]--;

用来消除子树贡献。dfs2统计其它子树对这颗子树的贡献。

void dfs2(int u,int f){
cnt[a[u]]++;
if(cnt[a[u]]==1){
sum-=color[a[u]];
num++;
}
ans[u]+=sum+num*tot;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f||vis[v])continue;
dfs2(v,u);
}
if(cnt[a[u]]==1){
sum+=color[a[u]];
num--;
}
cnt[a[u]]--;
}

如果这颗子树中出现一个颜色,并且它是第一次出现,那么减去所有子树的color[a[u]],加上其它子树的节点总数,因为每一条到其它子树的路径都会产生贡献,这也是我们一开始不考虑贡献对其他子树影响的原因,因为遍历子树的时候会把这些重复的贡献减去。

更具体还是看代码。

// luogu-judger-enable-o2
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int N=101000;
int Cnt,head[N];
int g[N],size[N],cnt[N],a[N],sum,color[N],tot,num,root,all,vis[N],ans[N],n;
struct edge{
int to,nxt;
}e[N*2];
void add_edge(int u,int v){
Cnt++;
e[Cnt].nxt=head[u];
e[Cnt].to=v;
head[u]=Cnt;
}
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
void getroot(int u,int f){
g[u]=0;size[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f||vis[v])continue;
getroot(v,u);
size[u]+=size[v];
g[u]=max(g[u],size[v]);
}
g[u]=max(g[u],all-size[u]);
if(g[u]<g[root])root=u;
}
void dfs1(int u,int f){
cnt[a[u]]++;
size[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f||vis[v])continue;
dfs1(v,u);
size[u]+=size[v];
}
if(cnt[a[u]]==1){
sum+=size[u];
color[a[u]]+=size[u];
}
cnt[a[u]]--;
}
void clear(int u,int f){
cnt[a[u]]++;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f||vis[v])continue;
clear(v,u);
}
if(cnt[a[u]]==1){
sum-=size[u];
color[a[u]]-=size[u];
}
cnt[a[u]]--;
}
void dfs2(int u,int f){
cnt[a[u]]++;
if(cnt[a[u]]==1){
sum-=color[a[u]];
num++;
}
ans[u]+=sum+num*tot;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f||vis[v])continue;
dfs2(v,u);
}
if(cnt[a[u]]==1){
sum+=color[a[u]];
num--;
}
cnt[a[u]]--;
}
void change(int u,int f,int k){
cnt[a[u]]++;
if(cnt[a[u]]==1){
sum+=k*size[u];color[a[u]]+=k*size[u];
}
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==f||vis[v])continue;
change(v,u,k);
}
cnt[a[u]]--;
}
void calc(int u){
dfs1(u,0);
ans[u]+=sum;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v])continue;
cnt[a[u]]++;
sum-=size[v];color[a[u]]-=size[v];
change(v,u,-1);
cnt[a[u]]--;
tot=size[u]-size[v];
dfs2(v,u);
cnt[a[u]]++;
sum+=size[v];color[a[u]]+=size[v];
change(v,u,1);
cnt[a[u]]--;
}
clear(u,0);
}
void work(int u){
calc(u);
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v])continue;
root=0,all=size[v];
getroot(v,0);
work(root);
}
}
signed main(){
n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<n;i++){
int u=read(),v=read();
add_edge(u,v);add_edge(v,u);
}
g[0]=n+10;root=0;all=n;
getroot(1,0);work(root);
for(int i=1;i<=n;i++)printf("%lld\n",ans[i]);
return 0;
}

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

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

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

  2. [LuoGu]P2664 树上游戏

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

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

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

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

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

  5. P2664 树上游戏

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

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

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

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

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

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

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

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

    code: #include <bits/stdc++.h> #define N 200009 #define ll long long #define setIO(s) freopen( ...

随机推荐

  1. 照片放大软件PhotoZoom怎么用?

    做设计时,难免常常要上网找一些图片素材,但是有时候我们找到图片效果不尽人意,图片风格符合设计要求的的却图片太小,用photoshop放大后,图片的色块像素就出来了,效果极为不理想,这就要我们将图片进行 ...

  2. 洛谷 p2618 数字工程 记忆化搜索_ 线性筛

    我们在线筛的同时处理出每个数的所有质因子,记忆化搜索的时候直接枚举质因子即可. 时间复杂度为 O(nlogn)O(nlogn)O(nlogn) Code: #include<cstdio> ...

  3. Pyhton学习——Day26

    #多态:多态指的是一类事物有多种形态# import abc# class Animal(metaclass = abc.ABCMeta):# 同一类事物:动物# @abc.abstractclass ...

  4. C# 基础复习 二 面向对象

    继承:子承父业  子:子类    父:父类    业:所有非私有成员 好处:代码的复用   继承后,实例化子类时,不止子类的构造,父类的构造也会执行,而且父类的构造先于子类的构造执行 即使在子类可以看 ...

  5. 为什么在index.jsp里面引入了common.js,在item-add.jsp以及其他一些jsp文件里面就不需要引入common.jsne ?

    那是因为,index.jsp页面的根节点是body,hrml.是一个完整的网页.那我们再看item-add.jsp页面,他节点是div,只是一个html的片段,并不是一个完整的网页,在easyUI中, ...

  6. 洛谷10月月赛II

    #A: P4924 [1007]魔法少女小Scarlet 这道题考了矩阵旋转 其实很考验推公式的能力和代码能力 这里有个小技巧 可以设(x, y)为原点,然后去推公式,然后实际操作中横坐标加上x,纵坐 ...

  7. 一个简单搜索引擎的搭建过程(Solr+Nutch+Hadoop)

    最近,因为未来工作的需要,我尝试安装部署了分布式爬虫系统Nutch,并配置了伪分布式的Hadoop来存储爬取的网页结果,用solr来对爬下来的网页进行搜索.我主要通过参考网上的相关资料进行安装部署的. ...

  8. Nutch的配置(使用MySQL作为数据存储)

    首先先从http://www.apache.org/dyn/closer.cgi/nutch/下载安装包 这里假定nutch的根目录为:${APACHE_NUTCH_HOME} 配置${APACHE_ ...

  9. 洛谷——P1352 没有上司的舞会

    https://www.luogu.org/problem/show?pid=1352#sub 题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树, ...

  10. [Oracle] Merge语句

    Merge的语法例如以下: MERGE [hint] INTO [schema .] table [t_alias] USING [schema .] { table | view | subquer ...