Solution
- 对于每种颜色 i ,计算出以每个点为起点的包含它的路径条数,每个点的答案为各种颜色加起来
- 删去颜色为 i 的所有点,树变成森林
- 每颗树内点间的路径不包含颜色 i ,不能对包含颜色 i 的路径条数产生贡献
- 不同树间的点,路径上必然有颜色 i
- 即颜色不为 i 的点,以它为起点的包含颜色 i 的路径条数为——n-删去颜色为 i 的所有点后它所在树的节点数
- 颜色为 i 的点,以它为起点的包含颜色 i 的路径条数为 n
- 第一次遍历,求出每个点去掉父亲颜色后所在树的节点数(该数以这个点为根)
- 特殊处理原树的根(它没有父亲,可看做去掉任何颜色后树的根)
- 第二次遍历,差分求出答案
Code
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N=1e5+10;
int head[N],nxt[N*2],ver[N*2],tot;
int si[N],tr[N],cut[N],n,col[N],u,v,d[N],cnt[N];
bool flag[N];
ll ans[N],sum;
void add(int x,int y)
{
ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
}
void dfs(int u,int fa)
{
int pre=cut[col[fa]];
si[u]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=ver[i];
if(fa==v) continue;
dfs(v,u);
si[u]+=si[v];
}
cut[col[u]]++;
if(!fa) return ;
tr[u]=si[u]-(cut[col[fa]]-pre);
cut[col[fa]]+=tr[u];
}
void solve(int u,int fa)
{
int pre=d[col[fa]];
d[col[fa]]=tr[u];
sum+=tr[u]-pre;
ll aa=n,bb=cnt[0],cc=sum,dd=d[col[u]];
ans[u]=aa*bb-cc+dd;
for(int i=head[u];i;i=nxt[i])
{
int v=ver[i];
if(v==fa) continue;
solve(v,u);
}
sum-=tr[u]-pre;
d[col[fa]]=pre;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&col[i]);
if(!flag[col[i]]) flag[col[i]]=true,cnt[++cnt[0]]=col[i];
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs(1,0);
for(int i=1;i<=cnt[0];i++) d[cnt[i]]=n-cut[cnt[i]],sum+=d[cnt[i]];
solve(1,0);
for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
return 0;
}
以它为起点的包含颜色 i 的路径条数为
来源:https://www.cnblogs.com/hsez-cyx/p/12400374.html