您的位置:首页 > 其它

BZOJ2730 矿场搭建

2015-06-04 15:38 162 查看
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2730

基本思路,注意到n非常的小,所以暴力即可。

首先显然我们要找出所有的割点,$O(n^2)$并查集。

我们将所有的割点删除,然后将剩余的边用并查集维护,化为很多个联通块。

有三种情况:

1.联通块和一个割点相连,这种情况下显然只能在当前联通块里任选一个点建一个逃生点。

2.与两个割点相连,这种情况下不用建逃脱点因为他只能割掉其中一个割点。

3.没有割点,至少建两个(任选两个点),因为有可能逃生点被破坏了,所以为两个。

至于情况数,因为同一联通块的点没有不同,所以直接乘起来。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>

#define N 510
#define p E[i].x
#define LL long long

using namespace std;

struct edge{
int x,to;
}E[N<<1];

int m,n,g
,totE,fa
,cnt
,siz
;
set<int> G
;
bool cut
;

inline void addedge(int x,int y){
E[++totE]=(edge){y,g[x]}; g[x]=totE;
E[++totE]=(edge){x,g[y]}; g[y]=totE;
}

int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}

void add(int x,int y){
int r1=find(x),r2=find(y);
if(r1!=r2) fa[r2]=r1;
}

bool check(int x){
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=2;i<=totE;i+=2)
if(E[i].x!=x&&E[i^1].x!=x) add(E[i].x,E[i^1].x);
int tmp=0;
for(int i=1;i<=n;i++){
if(i==x) continue;
if(!tmp) tmp=find(i);
if(find(i)!=tmp) return 1;
}
return 0;
}

int main(){

int caset=0;
while(scanf("%d",&m)==1){
if(!m) break;
for(int i=1;i<=n;i++){
cut[i]=0;
siz[i]=g[i]=cnt[i]=0;
G[i].clear();
}
printf("Case %d: ",++caset);
totE=1; n=0;
for(int i=1,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
addedge(x,y);
n=max(n,x); n=max(n,y);
}
bool fl=0;
for(int i=1;i<=n;i++)
if(check(i)) cut[i]=1,fl=1;
if(!fl){
printf("%d %lld\n",2,((n-1)*(LL)n)/2);
continue;
}
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=2;i<=totE;i+=2)
if(!cut[E[i].x]&&!cut[E[i^1].x]) add(E[i].x,E[i^1].x);
for(int x=1;x<=n;x++){
siz[find(x)]++;
if(!cut[x]) continue;
for(int i=g[x];i;i=E[i].to)
if(!cut[p]) G[find(p)].insert(x);
}
int ans=0;
LL ansv=1;
for(int i=1;i<=n;i++){
if(cut[i]) continue;
if(find(i)!=i) continue;
if(G[i].size()<2){
ans++;
ansv*=siz[i];
}
}
printf("%d %lld\n",ans,ansv);
}
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: