[HNOI2012]礦場搭建

嘟嘟嘟html

 

這道題一看就是跟tarjan有關的,又由於題中說某一個挖煤點坍塌,那也就是圖中刪去一個點以及和這個點鏈接的全部邊,因此確定跟割點有關。對於將割點刪去後圖中剩下的聯通塊,我剛開始想只要每個聯通塊有一個救援出口就好了。但後來發現這不符合出口最少(而後樣例還過了),由於若是一個塊鏈接大於等於2個割點的話,他實際上是不用設救援出口的,由於若是一個割點坍塌,這個塊能夠從另外一個割點出去,到別的塊中找出口。因此總結一下:node

在每個塊中dfs,統計和他鏈接的割點個數cnt:ios

1.cnt = 0:說明沒有割點,那就職意選兩個點做爲出口,方案數爲C(n, 2).git

2.cnt = 1:只和一個割點相連,就要設一個救援出口,方案數 * 塊中節點個數。ide

2.cnt = 2:不計入答案。post

 

而後就是一些代碼細節了:spa

1.這道題最坑的是不告你總節點數,因此得在輸入數據中求出總節點數和編號最大的節點(數據事實上節點是從1~max_node連續的)。code

2.多組數據,記得初始化,尤爲是上面的max_node(我由於這個一直90分,調了半個點兒)htm

3.方案數得開long long,雖然我明明感受不會爆int。blog

4.統計每個塊鏈接的不一樣割點個數別重了,仔細想一想怎麼解決,也能夠看下面的代碼。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a, x) memset(a, x, sizeof(a))
 15 #define rg register
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const int maxn = 1e5 + 5;
 21 inline ll read()
 22 {
 23     ll ans = 0;
 24     char ch = getchar(), last = ' ';
 25     while(!isdigit(ch)) {last = ch; ch = getchar();}
 26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 27     if(last == '-') ans = -ans;
 28     return ans;
 29 }
 30 inline void write(ll x)
 31 {
 32     if(x < 0) x = -x, putchar('-');
 33     if(x >= 10) write(x / 10);
 34     putchar(x % 10 + '0');
 35 }
 36 
 37 int n, m;
 38 vector<int> v[maxn];
 39 
 40 int dfn[maxn], low[maxn], cnt = 0;
 41 int root, cut[maxn];
 42 void tarjan(int now)
 43 {
 44     dfn[now] = low[now] = ++cnt;
 45     int flg = 0;
 46     for(int i = 0; i < (int)v[now].size(); ++i)
 47     {
 48         if(!dfn[v[now][i]])
 49         {
 50             tarjan(v[now][i]);
 51             low[now] = min(low[now], low[v[now][i]]);
 52             if(low[v[now][i]] >= dfn[now])
 53             {
 54                 flg++;
 55                 if(now != root || flg > 1) cut[now] = 1;
 56             }
 57         }
 58         else low[now] = min(low[now], dfn[v[now][i]]);
 59     }
 60 }
 61 
 62 bool vis[maxn];
 63 int Cnt = 0, flg = 0, pos = 0;        //pos實際上只記錄了第一個割點是誰
 64 void dfs(int now)
 65 {
 66     vis[now] = 1;
 67     Cnt++;
 68     for(int i = 0; i < (int)v[now].size(); ++i)
 69     {
 70         if(!vis[v[now][i]])
 71         {
 72             if(cut[v[now][i]])
 73             {
 74                 if(v[now][i] != pos)        
 75                 {
 76                     if(!flg) pos = v[now][i];
 77                     flg++;        //說明割點數已經大於一了,重複統計也沒有關係 
 78                 }
 79             } 
 80             else dfs(v[now][i]);
 81         }
 82     }
 83 }
 84 
 85 void init()
 86 {
 87     for(int i = 0; i < maxn; ++i) v[i].clear();
 88     Mem(dfn, 0); Mem(low, 0); Mem(cut, 0); Mem(vis, 0); 
 89     n = cnt = 0;
 90 }
 91 
 92 int main()
 93 {
 94     int CNT = 0;
 95     while(scanf("%d", &m) && m)
 96     {
 97         init();
 98         for(int i = 1; i <= m; ++i)
 99         {
100             int x = read(), y = read();
101             n = max(n, max(x, y));
102             v[x].push_back(y); v[y].push_back(x);
103         }
104         for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(root = i);
105         ll ans1 = 0, ans2 = 1;
106         for(int i = 1; i <= n; ++i) if(!cut[i] && !vis[i])
107         {
108             Cnt = 0; flg = 0; pos = 0; 
109             dfs(i);
110             if(flg == 0) ans1 += 2, ans2 *= (ll)Cnt * (ll)(Cnt - 1) >> 1;
111             if(flg == 1) ans1++, ans2 *= Cnt;
112         }
113         printf("Case %d: ", ++CNT);
114         write(ans1); space; write(ans2); enter;
115     }
116     return 0;
117 }
View Code
相關文章
相關標籤/搜索