luoguP3225 [HNOI2012]矿场搭建

传送门

刷这个题之前就觉得自己点双不太会QAQ

最后果然是点双写跪了……

题意与连通性有关+无向图 考虑tarjan->割点

自动想到分情况

对于每一个点双进行考虑

只有一个点肯定要放 方案数1

同理 如果一个割点都没有 需要放两个出口防止其中一个塌了 方案数C(sze,2)

如果只有有一个割点 那么一定要在里面放一个而且除了割点随便放(防止割点塌)方案数sze-1

如果有两个以上割点 那么一个都不用放 因为一个割点塌另一个也可以用

注意:

1.tot初值设为1……

2.我万年不用vector这次都用了(存点双所有点真恶心…)

3.tarjan点双弹栈谈到儿子(Debug的时候样例直接出一个sze=50000…)

Code:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<queue>
  6 #include<vector>
  7 #define ms(a,b) memset(a,b,sizeof a)
  8 #define rep(i,a,n) for(int i = a;i <= n;i++)
  9 #define per(i,n,a) for(int i = n;i >= a;i--)
 10 #define inf 2147483647
 11 using namespace std;
 12 typedef long long ll;
 13 ll read() {
 14     ll as = 0,fu = 1;
 15     char c = getchar();
 16     while(c < '0' || c > '9') {
 17         if(c == '-') fu = -1;
 18         c = getchar();
 19     }
 20     while(c >= '0' && c <= '9') {
 21         as = as * 10 + c - '0';
 22         c = getchar();
 23     }
 24     return as * fu;
 25 }
 26 const int N = 505;
 27 //head
 28 int n,m,T;
 29 int head[N],nxt[N<<1],mo[N<<1],cnt;
 30 void _add(int x,int y) {
 31     mo[++cnt] = y;
 32     nxt[cnt] = head[x];
 33     head[x] = cnt;
 34 }
 35 void add(int x,int y){if(x^y)_add(x,y),_add(y,x);}
 36 
 37 int dfn[N],low[N],stk[N],top,idx,scc;
 38 bool cut[N];
 39 
 40 vector<int> mem[N];
 41 
 42 void tarjan(int x,bool rt) {
 43     dfn[x] = low[x] = ++idx;
 44     stk[++top] = x;
 45     int cld = 0;
 46     for(int i = head[x];i;i = nxt[i]) {
 47     int sn = mo[i];
 48     if(!dfn[sn]) {
 49         tarjan(sn,0);
 50         low[x] = min(low[x],low[sn]);
 51         if(low[sn] == dfn[x]) {
 52         mem[++scc].push_back(x);
 53         int t = -1;
 54         while(t != sn) {
 55             t = stk[top--];
 56             mem[scc].push_back(t);
 57         }
 58         cld++;
 59         cut[x] = 1;
 60         }
 61     } else low[x] = min(low[x],dfn[sn]);
 62     }
 63     if(rt && cld < 2) cut[rt] = 0;
 64 }
 65 
 66 
 67 void clr() {
 68     rep(i,1,scc) mem[i].clear();
 69     ms(low,0),ms(dfn,0),ms(head,0),ms(cut,0);
 70     cnt = top = scc = idx = n = m = 0;
 71 }
 72 
 73 ll C2(ll n) {return n*(n-1)/2;}
 74 
 75 void solve() {
 76     clr();
 77     m = read();
 78     if(!m) exit(0);
 79     rep(i,1,m) {
 80     int x = read();
 81     int y = read();
 82     n = max(n,max(x,y));
 83     add(x,y);
 84     }
 85     int ans = 0;
 86     ll tot = 1;
 87     rep(i,1,n) if(!dfn[i]) top = 0,tarjan(i,1);
 88     rep(i,1,scc) {
 89     int num = 0;
 90     ll sze = (ll)mem[i].size();
 91     rep(j,0,sze-1) if(cut[mem[i][j]]) num++;
 92     if(sze == 1ll) ans++;
 93     else if(!num) ans += 2,tot *= C2(sze);
 94     else if(num == 1) ans++,tot *= sze - 1;
 95     }
 96     printf("Case %d: %d %lld\n",++T,ans,tot);
 97 }
 98     
 99 
100 int main() {
101     while(1) solve();
102 }

猜你喜欢

转载自www.cnblogs.com/yuyanjiaB/p/9756331.html
今日推荐