无向图上涂黑尽量少的点,使得任意删去一个点后,所有连通块中至少有一个被涂黑的点
做法:1.缩点求bcc和割顶 2.仅有一个割顶的bcc中标记一个非割顶点,即为所求。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<stack>
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 200100
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
int n,m;
vector<int> g[maxn],bcc[maxn];
int pre[maxn],low[maxn],bcc_cnt,dfs_clock,bccno[maxn],iscut[maxn];
int cas;
void add(int u,int v){ g[u].push_back(v); }
struct Edge{ int u,v; };
stack<Edge> S;
void dfs(int u,int fa){
int child=0; low[u]=pre[u]=++dfs_clock;
for(int i=0;i<g[u].size();i++){
int v=g[u][i]; Edge e=(Edge){u,v};
if(!pre[v]){
child++,S.push(e),dfs(v,u);
low[u]=min(low[u],low[v]);
if(pre[u]<=low[v]){
iscut[u]=1,bcc_cnt++,bcc[bcc_cnt].clear();
for(;;){
Edge x=S.top();S.pop();
if(bccno[x.u]!=bcc_cnt) { bcc[bcc_cnt].push_back(x.u);bccno[x.u]=bcc_cnt; }
if(bccno[x.v]!=bcc_cnt) { bcc[bcc_cnt].push_back(x.v);bccno[x.v]=bcc_cnt; }
if(x.u==u&&x.v==v) break;
}
}
}
else if(pre[v]<pre[u]&&v!=fa) { S.push(e);low[u]=min(low[u],pre[v]); }//pre[v]<pre[u]不能少
}
if(fa<0&&child==1) iscut[u]=0;//当根节点只有一个子节点时,不再是割点---不能少
}
void find_bcc(){
mem(pre,0),mem(iscut,0),mem(bccno,0),bcc_cnt=dfs_clock=0;
for(int i=1;i<=n;i++) if(!pre[i]) dfs(i,-1);
}
int main(){
//freopen("a.txt","r",stdin);
cas=0;
while(scanf("%d",&m)!=EOF){
if(!m) break;
n=0;
for(int i=1;i<=maxn;i++) g[i].clear();
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
n=max(n,a),n=max(n,b);
add(a,b),add(b,a);
}
find_bcc();
ll ans1=0,ans2=1;
for(int i=1;i<=bcc_cnt;i++){
int cut_cnt=0;
for(int j=0;j<bcc[i].size();j++){
if(iscut[bcc[i][j]]) cut_cnt++;
//printf("%d %daaa\n",i,bcc[i][j]);
}
if(cut_cnt==1) { ans2=(ll)((ll)ans2*(ll)(bcc[i].size()-1));ans1++; }
}
if(bcc_cnt==1){ ans1=2,ans2=(ll)((bcc[1].size()-1)*bcc[1].size()/2); }
printf("Case %d: %lld %lld\n",++cas,ans1,ans2);
}
return 0;
}