对无向图求边双连通分量,缩点后会形成一棵树。
但是这道题是删点不是删边,所以求的是点双连通分量,因为一个割点可能属于好几个点双连通分量,所以我们需要对每个点双连通分量新建节点,然后把属于这个点双连通分量的点连接上去。最后也可以形成一棵树,每两个原图中的节点中间一定连着一个点双的节点。
那么这道题方案怎么求呢?最小个数就是子树中不再含有点双节点的点双节点(或者说是点双中的叶子节点)。方案数就是每个对答案有贡献的点双的size(不包括割点)
有种情况需要特判,就是所有的节点缩成了一个点双,那么答案就是C(n,2)
#include
#include
#include
#include
#include
#define N 100000
#define LL long long
using namespace std;
int point[N],nxt[N],v[N],dfsn[N],low[N],n,m,tot,sz,top,st[N],belong[N],root;
int size[N],ins[N],cnt,x[N],y[N],mark[503][503],belong1[N],ct[N],vis[N],du[N];
LL C[503][503],ans;
int num;
struct data{
int point[N],nxt[N],v[N],tot;
void init(){
tot=0;
memset(point,0,sizeof(point));
}
void add(int x,int y){
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs(int x,int fa)
{
if (x>n) ct[x]=1;
else ct[x]=0;
for (int i=point[x];i;i=nxt[i]){
if (v[i]==fa) continue;
dfs(v[i],x);
ct[x]+=ct[v[i]];
size[x]++;
}
}
void solve()
{
dfs(root,0); num=0; ans=1;
for (int i=n+1;i<=n+cnt;i++)
if (ct[i]==1) num++,ans=(LL)ans*size[i];
}
}E;
void add(int x,int y)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void tarjan(int x)
{
dfsn[x]=low[x]=++sz; ins[x]=1; st[++top]=x;
for (int i=point[x];i;i=nxt[i]){
if (!dfsn[v[i]]) {
tarjan(v[i]);
low[x]=min(low[v[i]],low[x]);
if (dfsn[x]<=low[v[i]]) {
++cnt; int j;
while (1){
j=st[top--];
E.add(cnt+n,j); du[j]++;
belong[j]=cnt;
if (j==v[i]) break;
}
E.add(x,cnt+n); du[x]++; belong[x]=cnt;
}
}
else if (ins[v[i]]) low[x]=min(low[x],dfsn[v[i]]);
}
}
void clear()
{
E.init();
memset(du,0,sizeof(du));
memset(size,0,sizeof(size));
memset(ct,0,sizeof(ct));
memset(mark,0,sizeof(mark));
memset(low,0,sizeof(low));
memset(dfsn,0,sizeof(dfsn));
memset(belong,0,sizeof(belong));
memset(ins,0,sizeof(ins)); sz=cnt=top=0;
}
int main()
{
freopen("bzoj_2730.in","r",stdin);
freopen("bzoj_2730.out","w",stdout);
int T=0;
for (int i=0;i<=500;i++) C[i][0]=1;
for (int i=1;i<=500;i++)
for (int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
while (true){
scanf("%d",&m); tot=0; n=0; T++;
if (!m) break;
memset(point,0,sizeof(point));
for (int i=1;i<=m;i++) {
scanf("%d%d",&x[i],&y[i]);
add(x[i],y[i]); n=max(x[i],n);
n=max(y[i],n);
}
clear();
for (int i=1;i<=n;i++)
if (!dfsn[i]) tarjan(i);
root=1;
for (int i=1;i<=n;i++)
if (du[i]>=2) root=i;
E.solve();
if (cnt==1) {
num=2;
ans=C[n][2];
}
printf("Case %d: %d %lld\n",T,num,ans);
}
}