BZOJ1123 [POI2008]BLO(割点判断 + 点双联通缩点size)
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 typedef long long ll; 7 const int maxn = 1e5 + 5; 8 const int maxm = 5e5 + 5; 9 int dfn[maxn], low[maxn], head[maxn]; 10 ll ans[maxn], siz[maxn]; 11 int n, m, tot, num, root; 12 bool cut[maxn]; 13 struct edge{ 14 int to, next; 15 } ed[2*maxm]; 16 inline void init(){ 17 memset( head, -1, sizeof(head) ); 18 memset( dfn, 0, sizeof(dfn) ); 19 memset( cut, false, sizeof(cut) ); 20 memset( ans, 0, sizeof(ans) ); 21 num = 0; 22 tot = 1; 23 } 24 25 inline int min( int a, int b ){ 26 return a<b ? a:b; 27 } 28 29 inline void add( int u, int v ){ 30 tot ++; 31 ed[tot].to = v; 32 ed[tot].next = head[u]; 33 head[u] = tot; 34 } 35 36 inline void tarjan( int u ){ 37 dfn[u] = low[u] = ++num; 38 siz[u] = 1; 39 int flag = 0, sum = 0; 40 for( int i=head[u]; i!=-1; i=ed[i].next ){ 41 int v = ed[i].to; 42 if( !dfn[v] ){ 43 tarjan(v); 44 siz[u] += siz[v]; //计算子树的点个数 45 low[u] = min(low[u], low[v]); 46 if( dfn[u]<=low[v] ){ 47 flag ++; 48 ans[u] += (ll)siz[v]*(n-siz[v]); //if(cut[i]) ans = (i的子树, 除该子树的其他部分)的点对和 49 sum += siz[v]; 50 if( u!=root || flag>1 ) cut[u] = true; 51 } 52 }else low[u] = min(low[u], dfn[v]); 53 } 54 if( cut[u] ) ans[u] += (ll)(n-sum-1)*(sum+1) + (n-1); //如果去掉的点i是割点ans继续累加上(其他部分,i和i的子树)点对 + (i, 除i的其他部分)点对 55 else ans[u] = 2*(n-1); //if(!cut[i]) ans = 2*(n-1) 56 } 57 58 int main(){ 59 scanf("%d%d", &n, &m); 60 init(); 61 for( int i=0; i<m; i++ ){ 62 int u, v; 63 scanf("%d%d", &u, &v); 64 add( u, v ); 65 add( v, u ); 66 } 67 root = 1; 68 tarjan(1); 69 for( int i=1; i<=n; i++ ) 70 printf("%lld\n", ans[i]); 71 72 return 0; 73 } 74 /*Sample Input 75 5 5 76 1 2 77 2 3 78 1 3 79 3 4 80 4 5 81 Sample Output 82 8 83 8 84 16 85 14 86 8 87 */