【hdu6035】 Colorful Tree dfs序

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

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

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

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

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

显然,若树上所有的路径均经过该颜色的节点,则$S_i=\frac{n*(n-1)}{2}$。

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

该统计方法的时间复杂度为$O(n log n)$

 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 @ 2018-04-19 09:08  AlphaInf  阅读(165)  评论(0编辑  收藏  举报