要统计所有路径的信息,那我们考虑点分治,每次算经过分治中心的路径的贡献。然而路径的颜色数量实在是不好统计,既然只需要求从每个点出发的所有路径的颜色数量之和,那换一种思路,改为求从每个点出发包含某种颜色的路径数量之和。这两者显然是等价的。

  考虑在点分治过程中怎么算这个东西。首先算出每种颜色被多少条由根到分治块中的点的路径(特别地,根本身也是一条路径)包含。这个可以dfs求出,dfs时用桶记录一下当前出现了哪些颜色,若出现新颜色就记录并把该颜色的贡献加上当前点的子树大小。之后利用这个统计,计算某子树的答案时先把该子树贡献减去,dfs到某个点时把这个点的颜色的贡献改为由根到其他子树的路径条数,更新总贡献并更新该点的答案。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 100010
int n,color[N],p[N],size[N],cnt[N],tag[N],t=;
long long ans[N],tot;
bool flag[N];
struct data{int to,nxt;
}edge[N<<];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void makes(int k,int from)
{
size[k]=;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from&&!flag[edge[i].to])
{
makes(edge[i].to,k);
size[k]+=size[edge[i].to];
}
}
int findroot(int k,int s,int from)
{
int mx=;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from&&!flag[edge[i].to]&&size[edge[i].to]>size[mx]) mx=edge[i].to;
if ((size[mx]<<)>s) return findroot(mx,s,k);
else return k;
}
void calc(int k,int from,int v)
{
if (!tag[color[k]]) cnt[color[k]]+=size[k]*v,tot+=size[k]*v;
tag[color[k]]++;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from&&!flag[edge[i].to]) calc(edge[i].to,k,v);
tag[color[k]]--;
}
void work(int k,int from,int s)
{
int tmp=cnt[color[k]];tot+=s-cnt[color[k]];cnt[color[k]]=s;
ans[k]+=tot;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from&&!flag[edge[i].to]) work(edge[i].to,k,s);
cnt[color[k]]=tmp;tot-=s-cnt[color[k]];
}
void solve(int k)
{
makes(k,k);
k=findroot(k,size[k],k);flag[k]=;
makes(k,k);
tot=;
calc(k,k,);
ans[k]+=tot;
tag[color[k]]=;
for (int i=p[k];i;i=edge[i].nxt)
if (!flag[edge[i].to])
{
calc(edge[i].to,k,-);
cnt[color[k]]=size[k]-size[edge[i].to];tot-=size[edge[i].to];
work(edge[i].to,k,size[k]-size[edge[i].to]);
tot+=size[edge[i].to];cnt[color[k]]=size[k];
calc(edge[i].to,k,);
}
tag[color[k]]=;
calc(k,k,-);
for (int i=p[k];i;i=edge[i].nxt)
if (!flag[edge[i].to]) solve(edge[i].to);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read();
for (int i=;i<=n;i++) color[i]=read();
for (int i=;i<n;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
}
solve();
for (int i=;i<=n;i++) printf(LL,ans[i]);
return ;
}

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

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

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

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

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

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

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

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

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

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

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

  6. P2664 树上游戏

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

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

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

  8. LG2664 树上游戏

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

  9. poj1741 树上的点分治

    题意: 一棵10000个点的树,每条边的长不超过1000,给定一个值k,问距离不超过k的点对数有多少.(多组数据) 输入样例: 5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0输出样例: ...

随机推荐

  1. Luogu P1020 导弹拦截

    传送门 这道题信息量好大啊 1.Dilworth定理 Dilworth定理:对于一个偏序集,最少链划分等于最长反链长度. Dilworth定理的对偶定理:对于一个偏序集,其最少反链划分数等于其最长链的 ...

  2. shell脚本编程需要的知识

    关于shell的预备知识 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁即用户界面.用户把指令传给shell,然后shell再传输给系统内核,接着内核再去支配计算机硬件去执 ...

  3. 1-Android开发验证码(使用第三方Mob,注册)

    http://www.mob.com/ 注册以后 可以直接放在 可以下载到手机里面试一下了 后续就不截图了 补充: 自定义签名

  4. (转)/etc/init.d/functions详解

    转自:https://www.cnblogs.com/image-eye/archive/2011/10/26/2220405.html functions这个脚本是给/etc/init.d里边的文件 ...

  5. Ionic App ActionSheet布局问题

    使用 $ionicActionSheet的时候界面不像Ihpnoe手机那样显示的整齐,但是在电脑浏览器里面却是好的,原因还是Ionic自带css的问题,网上的答案是ionic.min.css/ioni ...

  6. Luogu P2257 YY的GCD

    莫比乌斯反演第一题.莫比乌斯反演入门 数论题不多BB,直接推导吧. 首先,发现题目所求\(ans=\sum_{i=1}^n\sum_{j=1}^m [\gcd(i,j)=prime]\) 考虑反演,我 ...

  7. [Spark][Python]Spark Join 小例子

    [training@localhost ~]$ hdfs dfs -cat people.json {"name":"Alice","pcode&qu ...

  8. Scala学习(四)练习

    映射和元组&练习 1. 设置一个映射,其中包含你想要的一些装备,以及它们的价格.然后构建另一个映射,采用同一组键,但在价格上打9折 映射代码如下: object HelloScala{ def ...

  9. Linq 之 Select 和 where 的用法

    最近开始学习linq.自己也总结一下,方便以后查阅. Select 同 Sql 中的 select 类似,即输出我们要的东东,感觉在 linq 中更加强大. Linq 可以对集合如数组.泛型等操作,这 ...

  10. python基础知识小结-运维笔记

    接触python已有一段时间了,下面针对python基础知识的使用做一完整梳理:1)避免‘\n’等特殊字符的两种方式: a)利用转义字符‘\’ b)利用原始字符‘r’ print r'c:\now' ...