您的位置:首页 > 其它

hdu3671 Boonie and Clyde (Tarjan求割点)

2015-10-12 16:37 295 查看
http://acm.hdu.edu.cn/showproblem.php?pid=3671

题目大意:给定连通无向图,求可行的点对的数量,该点对可以使图在删去该点对后剩下的图中,至少有一对点不连通。

Tarjan算法求强连通图的复杂度是O(n),如果直接枚举两个点并且求连通的话,总的复杂度为O(n^3)

如果先删去一个点,如果剩下的图分成了二个以上的块,则认为只要删去了这个点,剩下n-1个点无论删去哪个都是有效答案。因此对答案贡献为n-1

如果分成了两个块,则要分情况讨论。如果有一块只有一个点,则删去这个点后,原来两个块又变成了一个块,此时这种情况对答案的贡献是n-2。如果两个块都只有一个点,则这个图在删点前就只有3个点,是无解的。

如果分成一个块,则按照正常的Tarjan求割点流程求割点数量即可。

因为每一对点都有重复统计一次,最后答案要除以2

Tarjan求割点流程:

在遍历树中,如果一个非根节点u的子节点v,有DFN[v]<=LOW[u],证明该子节点不能通过一个回边回到节点u的上层。此时该节点u即为割点。

对于根节点,若它的子树数量>1,则它也是割点。

#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<sstream>
#include<stack>
#include<queue>
#include<fstream>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<functional>
#include<cmath>
#define CLR(x) memset(x,0,sizeof(x))
#define SETMAX(x) memset(x,0x3f,sizeof(x))
#define SETNO(x) memset(x,-1,sizeof(x))
#define ll long long
#define eps 3e-12
#define pow2(x) ((x)*(x))
#define forto(i,n) for(int i=0;i<n;i++)
#define for1to(i,n) for(int i=1;i<=n;i++)
#define VI vector<int>
using namespace std;
const double PI=acos(-1.0);
#define INF 0x3f3f3f3f
#define NINF 0xbfffffff

int p[1111];
int to[21111];
int bro[21111];
int tot;
void addEdge(int a,int b)
{
to[++tot]=b;
bro[tot]=p[a];
p[a]=tot;
}

int rem;

int index;
int DFN[1111];
int LOW[1111];

int root;
int ans;
int tarjan(int s)
{
DFN[s]=LOW[s]=++index;
int sons=0;
int tp=1;
for(int t=p[s];t;t=bro[t])
{
int tar=to[t];
if (rem==tar)
continue;
if (!DFN[tar])
{
sons++;
tp+=tarjan(tar);
LOW[s]=min(LOW[s],LOW[tar]);
if (s!=root&&DFN[s]<=LOW[tar])
ans++;
}
else
LOW[s]=min(LOW[s],DFN[tar]);

}

if (s==root&&sons>1)
ans++;
return tp;
}

int main()
{
ios_base::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
//freopen("test.txt","r",stdin);
#endif // ONLINE_JUDGE
int n,m;
int C=1;
while(scanf("%d%d",&n,&m),n||m)
{
CLR(p);
tot=0;
forto(i,m)
{
int a,b;
scanf("%d%d",&a,&b);
addEdge(a,b);
addEdge(b,a);
}
int result=0;
for(int i=1;i<=n;i++)
{
int res[1111];
int resp=0;
CLR(DFN);
index=1;
rem=i;
ans=0;
for(int j=1;j<=n;j++)
if (j!=rem&&!DFN[j])
{
root=j;
res[resp++]=tarjan(j);
}
if (resp>=3)
{
result+=n-1;
}
if (resp==2)
{
result+=n-1;
if (res[0]==1)
result--;
if (res[1]==1)
result--;
}
if (resp==1)
result+=ans;
}
printf("Case %d: %d\n",C++,result/2);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm