思路:
- 此题不是一般的求割点,而是求去掉一对点之后能否让图分开,即割点对(我这造词能力,满分)
- 对于第一个点我们只能枚举,但是对于第二点却不必暴力枚举
- 考虑经过删减第一个点之后的图,如果删去这个点之后剩下了一个部分,那么我们对这张图tarjan求割点,然后加入结果就好了。如果剩下了2个部分,那么考虑这两个部分中的点的个数,如果两个部分中的点都是1个,那么对结果没有贡献。如果只有一个部分是1个点,那么对结果的贡献是n-2。如果都是大于1个点的,那么这两个部分中的任何一点都可以和一开始我们删的点满足割点对,即对结果贡献为n-1
- 考虑出现3个及以上部分的情况,那么我们可以随意取第二个点,一定符合题意的。对结果贡献n-1
- 我们将点对考虑了两次,所以最后结果要除以2
割边割点
- 割点时需要满足的条件 dfn[u]<=low[v] //v节点最多回到我这里,所以u为割点(u是割点,所以注意别加多了)
- 割边时需要满足的条件 dfn[u]<low[v] //v节点最多回到他自己,所以
<u,v>
为割边 - 注意: 判断割点时,对当前节点是否为根做讨论,如果为根,那么还得满足子节点的个数大于1。
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
int cntcv;
const int maxn = 10100;
int head[maxn];
struct edge{
int to,v,next;
} edg[50100];
int cnte;
void addedge(int x,int y,int v){
edg[++cnte].to = y;
edg[cnte].v = v;
edg[cnte].next = head[x];
head[x] = cnte;
}
int cancel;
int cntc;
int dfn[maxn];
int low[maxn];
int stak[maxn];
int visstak[maxn];
int cntstak = 0;
int belong[maxn];
int out[maxn];
int index;
int jishu;
void tarjan(int u,int f){
jishu++;
dfn[u] = low[u] = ++index;
stak[cntstak++] = u;
visstak[u] = 1;
int flag = 0,chil = 0;
for(int i = head[u]; i != -1;i = edg[i].next){
int v = edg[i].to;
if(v == f || v == cancel) continue;
chil++;
if(!dfn[v]){
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(flag == 0 && dfn[u] <= low[v] && f != -1){
cntcv++;flag = 1;
}
else if(flag == 0 && dfn[u] <= low[v] && chil > 1){
cntcv++;flag = 1;
}
}
else if(visstak[v]){
low[u] = min(low[u],dfn[v]);
}
}
if(dfn[u] == low[u]){
cntc++;
int v;
do{
v = stak[--cntstak];
visstak[v] = 0;
}
while(v != u);
}
}
void ini(){
cntcv = 0;
index = 0;
cntstak = 0;
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(visstak,0,sizeof(visstak));
}
int main(){
int n,m;
int nca = 0;
while(scanf("%d%d",&n,&m),n||m){
int shuchu = 0;
cnte = 0;
memset(head,-1,sizeof(head));
nca++;
int a,b;
for(int i = 1; i <= m; i++){
scanf("%d%d",&a,&b);
addedge(a,b,1);
addedge(b,a,1);
}
for(cancel = 1;cancel <= n;cancel++){
ini();
int count = 0;
int cntofjishu = 0;
for(int i = 1;i <= n;i++){
if(!dfn[i] && i != cancel){
tarjan(i,-1);
count++;
if(jishu == 1){
cntofjishu++;
}
jishu = 0;
}
}
if(count == 1){
shuchu+=cntcv;
}
else if(count == 2){
if(cntofjishu == 2){
}
else if(cntofjishu == 1){
shuchu += n-2;
}
else{
shuchu += n-1;
}
}
else{
shuchu += n-1;
}
}
printf("Case %d: %d\n",nca,shuchu/2);
}
return 0;
}