P3225 [HNOI2012]礦場搭建 題解
阿新 • • 發佈:2021-08-23
題目大意
給定一個由隧道連線挖煤點的無向圖,保證在任何一個工地發生坍塌後,其他的挖煤點的工人都有一條道路通向救援出口
求最少的救援出口處和總方案數
問題求解
顯然
如果沒有割點
至少需要建立兩個出口
從任意非割點的地方選擇兩個點建立
如果這個分組只有一個割點
只需要在分組內設立一個出口
可以設立在任意一個非割點的地方
如果有兩個及以上個割點,則無需建立,可以直接到達其他聯通塊
程式碼實現
#include<cstdio> #include<vector> #include<stack> #include<cstring> using namespace std; typedef long long LL; const int maxn=505; int T,N,M,K,cnt,Ans1,c[maxn],low[maxn],dfn[maxn],root; LL Ans2; vector<int> e[maxn],DCC[maxn]; stack<int> s; inline int read(){ int ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();} while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar(); return ret*f; } void Tarjan(int x){ int fa=0; s.push(x); low[x]=dfn[x]=++cnt; for(int j=0;j<e[x].size();j++){ int y=e[x][j]; if(!dfn[y]){ Tarjan(y); low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]){ fa++;K++; if(x!=root||fa>1)c[x]=1; DCC[K].push_back(x); int z=0; do{ DCC[K].push_back(z=s.top());s.pop(); }while(z!=y); } } else low[x]=min(low[x],dfn[y]); } } int main(){ freopen("P3225.in","r",stdin); freopen("P3225.out","w",stdout); while(true){ N=read();if(!N)break; while(!s.empty()) s.pop(); for(int i=1;i<=N;i++) e[i].clear(); for(int i=1;i<=K;i++) DCC[i].clear(); M=K=Ans1=cnt=0;Ans2=1; memset(low,0,sizeof low); memset(c,0,sizeof c); memset(dfn,0,sizeof dfn); for(int i=1;i<=N;i++){ int x=read(),y=read(); e[x].push_back(y);e[y].push_back(x); M=max(M,x);M=max(M,y); } for(int i=1;i<=M;i++)if(!dfn[i]){ root=i;Tarjan(i); } for(int i=1;i<=K;i++){ int num=0,cnt=DCC[i].size(); for(int j=0;j<cnt;j++)num+=c[DCC[i][j]]; // for(int j=0;j<cnt;j++)printf("%d ",DCC[i][j]); printf("\n"); if(num==1)Ans1++,Ans2*=cnt-1; if(num==0)Ans1+=2,Ans2*=cnt*(cnt-1)>>1; } // printf("%d %lld\n",Ans1,Ans2); printf("Case %d: %d %lld\n",++T,Ans1,Ans2); } return 0; }
關於其他
貌似有三倍經驗???