前(sai)记(hua)
第一次见到这道题是在某大佬的博客里(那时它还叫UVALIVE5135),以为它是点双连通的裸题(然而我最后没有用点双),没调出来,未果。
在班上大佬讲课时又遇见它【然后它变成了bzoj的题?】了,调出来了
解法
在一个连通图有多个连通分量时
该连通图矿井个数为割点为1的连通图的个数。
方案数为割点为1的连通图的点数-1(割点)相乘
在一个连通图只有一个连通分量时
该连通图的矿井个数为2
方案数为c(2,总点数)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e4+5,M=5e4+5;
bool iscut[N],vis[N];
int low[N],dfn[N],idc;
int head[N],nxt[M],to[M],etot;
int u[N],v[N],c[N],cut[N],tot;
void adde(int u,int v)
{
to[++etot]=v;
nxt[etot]=head[u];
head[u]=etot;
}
void tarjan(int u,int fa)
{
int son=0;
low[u]=dfn[u]=++idc;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
if(!dfn[v]){
son++;
tarjan(v,u);
low[u]=min(low[v],low[u]);
if(low[v]>=dfn[u]) iscut[u]=1;
}
else low[u]=min(low[u],dfn[v]);
}
if(u==fa&&son<=1) iscut[u]=0;
}
int cnt;
int dfs(int u,int sta)
{
int ans=0;
vis[u]=1;cnt++;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(vis[v]) continue;
if(iscut[v]){
if(cut[v]!=sta) {cut[v]=sta;ans++;}
continue;
}
ans+=dfs(v,sta);
}
return ans;
}
void init()
{
etot=0;idc=0;tot=0;
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(iscut,0,sizeof(iscut));
memset(cut,0,sizeof(cut));
}
int main()
{
int n,k=0;
while(scanf("%d",&n)&&n){
k++;
init();
for(int i=1;i<=n;i++){
scanf("%d%d",&u[i],&v[i]);
c[++tot]=u[i],c[++tot]=v[i];
}
sort(c+1,c+1+n*2);
int sum=unique(c+1,c+1+n*2)-c-1;
for(int i=1;i<=n;i++){
u[i]=lower_bound(c+1,c+1+sum,u[i])-c;
v[i]=lower_bound(c+1,c+1+sum,v[i])-c;
adde(u[i],v[i]);
adde(v[i],u[i]);
}
tarjan(1,1);
int exit=0;long long ans=1;
for(int i=1;i<=sum;i++)
if(!vis[i]&&!iscut[i]){
cnt=0;
int x=dfs(i,i);
if(x==1) {
exit++;
ans=(long long)ans*cnt;
}
if(!x){
exit+=2;
ans=(long long)ans*cnt*(cnt-1)/2;
}
}
printf("Case %d: %d %lld\n",k,exit,ans);
//bzoj要改成lld?
}
return 0;
}