【POI2008】BLO

这道题是在基本的tarjan求割点的算法上进一步加深,加上了计数问题。

基本思路是显然的,我们必定要跑一遍tarjan求割点,之后我们就要分类讨论。

若这个点不是割点,那么把他的边去掉之后这个点就与原来的联通块分开了,一共有(n-1)对,因为(x,y),(y,x)算两对,所以答案为n-1<<1

若这个点是割点,那么把他的边去掉之后这张图就变成了若干部分:这个点自己、在搜索树中这个点的儿子所在的子树、除了以上两部分的联通块。

因此,我们可以在tarjan的同时求出搜索树中每一棵子树的大小size,由以上总结得到,ans为以上三部分每一部分与剩下的相乘之后再求和即可。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 typedef long long ll;
 7 inline int read() {
 8     int ret=0;
 9     int op=1;
10     char c=getchar();
11     while(c<'0'||c>'9') {if(c=='-') op=-1; c=getchar();}
12     while(c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
13     return ret*op;
14 }
15 struct node {
16     int next,to;
17 }a[500010<<1];
18 int n,m,head[500010<<1],num,tot;
19 int dfn[100010],low[100010],size[100010];
20 ll ans[100010];
21 int vis[100010];
22 void add(int from,int to) {
23     a[++num].next=head[from]; a[num].to=to; head[from]=num;
24     swap(from,to);
25     a[++num].next=head[from]; a[num].to=to; head[from]=num;
26 }
27 void tarjan(int u) {
28     dfn[u]=low[u]=++tot;
29     int pd=0,sum=0;
30     size[u]=1;
31     for(int i=head[u];i;i=a[i].next) {
32         int v=a[i].to;
33         if(!dfn[v]) {
34             tarjan(v);
35             low[u]=min(low[u],low[v]);
36             size[u]+=size[v];
37             if(low[v]>=dfn[u]) {
38                 pd++;
39                 ans[u]+=(long long)size[v]*(n-size[v]);
40                 sum+=size[v];
41                 if(u!=1||pd>1) vis[u]=1;
42             }
43         }
44         else low[u]=min(low[u],dfn[v]);
45     }
46     if(vis[u]) ans[u]+=(long long)(n-sum-1)*(sum+1)+n-1;
47     else ans[u]=(n-1)<<1;
48 }
49 int main() {
50     n=read(); m=read();
51     for(int i=1;i<=m;i++) {
52         add(read(),read());
53     }
54     tarjan(1);
55     for(int i=1;i<=n;i++)
56         printf("%lld
",ans[i]);
57     return 0;
58 }
AC Code
原文地址:https://www.cnblogs.com/shl-blog/p/10807580.html