luogu P3225 [HNOI2012]矿场搭建
题面传送门
感觉这道紫题质量还是可以的。
我们先跑一遍\(Tarjan\)求出图的割点,然后根据割点将图割开。对于每个连通块,分类讨论:
如果没有接触到割点,那么要设两个点,因为要考虑原来那个塌了的情况。方案数乘上\(C^{2}_{n}\)
如果只有一个割点,那么为了防止割点崩塌,得设一个,方案数乘上\(n\)
如果有两个以上割点,那么无论如何都能跑回去,所以不用设割点。
代码实现:
#include<cstdio>
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,x,y,t,nows,c[1039],dfn[1039],low[1039],vis[1039],now,b[1039],head,h[1039],flag[1039],num;
unsigned long long tot,pus,ans,tots;
struct yyy{
int to,z;
}f[1039],tmp;
inline void add(int x,int y){
f[++head]=(yyy){y,h[x]};
h[x]=head;
}
inline void tarjan(int x,int last){
dfn[x]=low[x]=++tot;
vis[x]=1;
int cur=h[x],pus=0;
yyy tmp;
while(cur!=-1){
tmp=f[cur];
if(tmp.to!=last){
if(!dfn[tmp.to]) {
tarjan(tmp.to,x);low[x]=min(low[x],low[tmp.to]);
if(low[tmp.to]>=dfn[x]&&!b[x]&&x!=last) b[x]=1;
pus++;
}
else low[x]=min(low[x],dfn[tmp.to]);
}
cur=tmp.z;
}
if(last==x&&pus>=2) b[x]=1;
}
inline void dfs(int x){
if(b[x]){flag[x]=nows;tot++;return;}
int cur=h[x];
pus++;
flag[x]=nows;
yyy tmp;
while(cur!=-1){
tmp=f[cur];
if(flag[tmp.to]!=nows) dfs(tmp.to);
cur=tmp.z;
}
}
int main(){
register int i;
scanf("%d",&n);
while(n){
num++;
tots=m=tot=nows=0;ans=1;
memset(h,-1,sizeof(h));
memset(b,0,sizeof(b));
memset(flag,0,sizeof(flag));
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
head=0;
for(i=1;i<=n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x),c[x]=c[y]=1,m=max(m,x),m=max(m,y);
for(i=1;i<=m;i++) if(!dfn[i]&&c[i]) tarjan(i,i);
//for(i=1;i<=m;i++) if(b[i]) printf("%d ",i);
//printf("\n");
for(i=1;i<=m;i++){
if(!b[i]&&!flag[i]&&c[i]){
tot=pus=0;nows++;
dfs(i);
// printf("%d %llu %llu\n",i,tot,pus);
if(!tot) ans*=max(pus*(pus-1)/2,1),tots+=min(2,pus);
else if(tot==1) ans*=pus,tots++;
}
}
printf("Case %d: %llu %llu\n",num,tots,ans);
scanf("%d",&n);
}
}