1. 程式人生 > >BZOJ2430&&洛谷P3225[HNOI2012]礦場搭建

BZOJ2430&&洛谷P3225[HNOI2012]礦場搭建

無向圖tarjan求割點

很顯然,我們求出所有聯通分量後,若這個聯通分量內有

1個割點,那麼只能在這個點上建出口,因為這個點壞了其他點就出不去了

2+個割點,那麼這個聯通分量是安全的,因為其中一個壞了還能走另一個

0個割點,那麼這個聯通分量至少需要兩個出口,否則也無法出去

然後方案數就根據上面這些亂搞一下就好了

程式碼

//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lli long long int
using namespace std; const int M=200500; int n,m,ans,emm; int to[M],nxt[M],head[M],cnt; int dfn[M],low[M],cut[M],fa[M],ind,ti; inline int read() { int x=0;char ch=getchar(); while (ch>'9'||ch<'0') ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } inline void
add(int x,int y) { to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt; to[++cnt]=x;nxt[cnt]=head[y];head[y]=cnt; return ; } inline void tarjan(int x,int f) { low[x]=dfn[x]=++ind;int son=0; for (int i=head[x];i;i=nxt[i]) if (!dfn[to[i]]) { tarjan(to[i],x);son++; low[x]=min(low[x],low[to[i]]); if (
low[to[i]]>=dfn[x]) cut[x]=1; } else if (f!=to[i]) low[x]=min(low[x],dfn[to[i]]); if (son==1&&!f) cut[x]=0; return ; } inline void dfs(int x) { fa[x]=ti; if (cut[x]) return ;emm++; for (int i=head[x];i;i=nxt[i]) { if (fa[to[i]]!=ti&&cut[to[i]]) ans++,fa[to[i]]=ti; if (!fa[to[i]]&&!cut[to[i]]) dfs(to[i]); } return ; } inline void clean() { fill(low,low+n+1,0); fill(dfn,dfn+n+1,0);fill(cut,cut+n+1,0); fill(head,head+n+1,0);fill(fa,fa+n+1,0); return (void)(ti=ind=n=cnt=0); } signed main() { int cas=0; while ((m=read())&&m) { int x,y;clean(); lli qlm=0,que=1; for (int i=1;i<=m;i++) { x=read(),y=read(); add(x,y),n=max(n,max(x,y)); } for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i,0); for (int i=1;i<=n;i++) if (!fa[i]&&!cut[i]) { ti++;ans=emm=0;dfs(i); if (!ans) qlm+=2,que*=(emm*(emm-1)/2); if (ans==1) qlm++,que*=emm; } printf("Case %d: %lld %lld\n",++cas,qlm,que); } return 0; }