hdu3671 Boonie and Clyde (Tarjan求割点)

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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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
#define for1to(i,n) for(int i=1;i<=n;i++)
#define VI vector
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;
}

你可能感兴趣的:(图论,tarjan,acm)