【hdu6035】 Colorful Tree dfs序

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6035

题目大意:给你一棵树,树上每个节点都有一个颜色。 现在定义两点间的距离为两点最短路径上颜色集合大小,求该树上所有点对的距离之和。其中树上的节点个数2105

如果直接处理每一条路径上颜色集合大小,显然比较麻烦。我们不妨换一种思路。

我们用S_i表示经过颜色i的路径的数量,显然答案=Si

考虑如何求S_i。我们先将所有颜色为i的节点全部找出来,按照dfs序排序。

显然,若树上所有的路径均经过该颜色的节点,则Si=n(n1)2

对于该点集中的节点x的每一棵子树,不妨设当前子树的根节点为v,找出所有点集中满足dfn[v]<dfn[u]low[v]且不存在y,使得dfn[v]<dfn[y]<dfn[u]low[v]的所有的u,则显然有(siz[v]siz[u])×(siz[v]siz[u]1)2个点对不会对答案产生贡献。其中siz[x]表示以x为根的子树的节点个数。

该统计方法的时间复杂度为O(nlogn)

复制代码
 1 #include<bits/stdc++.h>
 2 #define L long long
 3 #define M 200005
 4 using namespace std;
 5 struct edge{int u,next;}e[M*2]={0}; int head[M]={0},use=0;
 6 void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
 7 
 8 int dfn[M]={0},low[M]={0},t=0;
 9 int siz[M]={0},col[M]={0}; 
10 
11 void dfss(int x,int fa){
12     dfn[x]=++t; siz[x]=1;
13     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
14         dfss(e[i].u,x);
15         siz[x]+=siz[e[i].u];
16     }
17     low[x]=t;
18 }
19 bool cmp(int x,int y){
20     if(col[x]==col[y]) return dfn[x]<dfn[y];
21     return col[x]<col[y];
22 }
23 int p[M]={0};
24 L ans=0,sum=0,n;
25 
26 void dfs(int &x,int y){
27     int xx=p[x]; x++;
28     for(int i=head[xx];i;i=e[i].next)
29     if(dfn[xx]<dfn[e[i].u]){
30         int v=e[i].u;
31         L cnt=siz[v];
32         while(x<=y&&dfn[p[x]]<=low[v]){
33             cnt-=siz[p[x]];
34             dfs(x,y);
35         }
36         sum-=cnt*(cnt-1);
37     }
38 }
39 int hh=0;
40 int Main(){
41     hh++;
42     ans=0; sum=0; t=0;
43     memset(head,0,sizeof(head)); use=0;
44     for(int i=1;i<=n;i++) scanf("%d",col+i);
45     for(int i=1;i<n;i++){
46         int x,y; scanf("%d%d",&x,&y);
47         add(x,y); add(y,x);
48     }
49     dfss(1,0); add(0,1);
50     for(int i=1;i<=n;i++) p[i]=i;
51     sort(p+1,p+n+1,cmp);
52     for(int i=1,j;i<=n;i=j+1){
53         for(j=i;col[p[i]]==col[p[j]];j++); j--;
54         sum=n*(n-1);
55         p[--i]=0;
56         dfs(i,j);
57         ans+=sum;
58     }
59     printf("Case #%d: %lld\n",hh,ans/2);
60     //cout<<ans/2<<endl;
61 }
62 
63 int main(){
64     freopen("in.txt","r",stdin);
65     while(cin>>n) Main();
66 }
复制代码

 

posted @   AlphaInf  阅读(170)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示