BZOJ2730_矿场搭建_KEY
这道题用Tarjan求出点-双连通分量,对联通块做一些玄学操作。
细节很多,不说了看code。
code:
/************************************************************** Problem: 2730 User: yekehe Language: C++ Result: Accepted Time:0 ms Memory:840 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int read() { char c;while(c=getchar(),c<'0'||c>'9'); int x=c-'0';while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0'; return x; } int N; struct list{ int head[505],nxt[505],To[505],cnt; void clear(){memset(head,-1,sizeof head);memset(nxt,-1,sizeof nxt);cnt=0;} void add(int x,int y) { To[cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; cnt++; } }W; int DFN[505],LOW[505],cnt,wif[505]; void tarjan(int now,int fa) { int child=0; DFN[now]=LOW[now]=++cnt; for(int i=W.head[now];i!=-1;i=W.nxt[i]){ if(!DFN[W.To[i]]){ child++; tarjan(W.To[i],now); LOW[now]=min(LOW[now],LOW[W.To[i]]); if(LOW[W.To[i]]>=DFN[now])wif[now]=1;//割顶的判定 } else if(DFN[W.To[i]]<DFN[now]&&W.To[i]!=fa){ LOW[now]=min(LOW[now],DFN[W.To[i]]); } } if(fa<0&&child==1)wif[now]=0; }//求点-双连通分量 int ct=0; int vis[505],tong[505]; long long cnc; void dfs(int now) { cnc++;//求除了割点的联通块内的其他点的数量 vis[now]=1;//标记该点被访问 for(int i=W.head[now];i!=-1;i=W.nxt[i]){ if(!vis[W.To[i]]) if(wif[W.To[i]])tong[W.To[i]]=ct;//用桶记录割顶 else dfs(W.To[i]); } } int main() { int TOT=0; while((N=read())!=0){ TOT++; W.clear(); memset(DFN,0,sizeof DFN); memset(LOW,0,sizeof LOW); memset(tong,0,sizeof tong); memset(wif,0,sizeof wif); memset(vis,0,sizeof vis); cnt=0; int TN=0; for(int i=1;i<=N;i++){ int x=read(),y=read(); TN=max(TN,x);TN=max(TN,y); W.add(x,y);W.add(y,x);//双向边 } for(int i=1;i<=TN;i++) if(!DFN[i])tarjan(i,-1); int ans=0; printf("Case %d: ",TOT); long long tot=1;//方案数
for(int i=1;i<=TN;i++)if(!vis[i]&&!wif[i]){//这个点既不是割顶也没被访问过 cnc=0;//联通块内除了割顶的点的数量 dfs(i); int kkk=0;//求有几个割顶 for(int j=1;j<=TN;j++)if(tong[j]==ct)kkk++; if(!kkk){//如果这个联通块内没有割顶,那么要且只要建两个救助站就好了,可以自己画图 ans+=2; tot=tot*cnc*(cnc-1)/2;//用乘法原理和组合求方案数 } else { if(kkk==1){//有一个割顶 ans++; tot=tot*cnc;//乘以联通块内除了割顶的点的数量 } }//当割顶的数量大于等于2时不能也不需要建救助站。不能是因为要求数量最少,可以自己画图 } printf("%d %lld\n",ans,tot); } }