[HNOI2012]矿场搭建 解题报告

第一次听说点双连通分量这个概念。。
点双连通分量是说这个连通分量中任意两个点之间都存在两条不经过同一个点的路径。
点双连通分量比边双连通分量要麻烦很多的问题在于不能缩点,因为两个点双连通分量可能会由一个割点连起来,当然也可能由一条桥边连起来,这样的话就比较麻烦。

这个题的话,假如不存在没有割点的点双连通分量。对于那种只有1个割点的点双连通分量,假如我把这个割点删掉,那么它必须是要从内部找一个点的;而对于有大于1个割点的点双连通分量,我删掉这个点双连通分量里任意一个点,都可以从一个割点出去直到一个只有1个割点的点双连通分量,所以答案就是只有1个割点的点双连通分量分量的个数。(这里方便起见,我们认为只有两个点、一条边相连的连通分量是一个点双连通分量)然后我们考虑没有割点的点双连通分量,因为它没有割点,也就是说它与外界是独立的,如果它的大小大于1的话,我们需要选2个点。
然而其实我们没有必要把点双连通分量真的求出来。因为我们只需要考虑割点数小于等于1的极大双连通分量,而这种双连通分量它的非割点部分必然是存在的(因为割点的度至少为2,而我们上文约定了一条边也是一个极大双连通分量),所以我们考虑与非割点部分直接相连的割点数量等价于考虑原极大双连通分量中的割点数量了(但是并非所有与非割点部分直接相连的割点就是这个极大点双连通分量中的所有割点)。这是因为由tarjan求点双连通分量的过程知道,一个极大点双连通分量中与外界有边相连的点等价于割点;所以与一个非割点部分直接相连的割点必然是小于等于该极大点双连通分量中所有的割点的,而如果前者为0的话,后者显然也为0,如果前者为1的话,后者必然也为1(因为假如后者大于1的话,把割点分成与非割点部分相连的那1个与其他割点,那么其他割点与非割点部分的路径必然会经过那个特殊的割点,就与点双连通分量的定义相悖了),所以其实只有当前者大于1的时候才会有后者大于前者的情况出现,不过我们这里需要考虑的只是它是否大于1,所以这种情况对答案并没有影响。
做这道题的时候还发现了一条有趣的性质:一个点集S(|S|≥3)是点/边双连通分量<=>对于任意(a,b,c),a,b,c∈V,存在一条a->b->c的点/边简单路径。(=>)我们不妨找到一条b->a的路径p1和两条b->c且不相交的路径p2,p3,然后我们设b->a的路径与这两条路径的交的最后一个顶点是u,在p2上,那么a->p2->u->p1->b->p3->c就是一个合法解;(<=)假如说不是点/边双连通分量,那么就意味着存在一对顶点(a,b),使得使得它们之间的路径必然会经过一个点/边,即这个图被这个点/边分成了两部分,那么我们不妨在a这一部分中选另一个点c,那么a->b->c必然要经过这个点/边两次,不可能存在一条点/边简单路径。
代码:

#include<stdio.h>
#include<iostream>
using namespace std;
#include<cstring>
#include<algorithm>
const int N=500+5,M=500+5;
int next[M<<1],succ[M<<1],ptr[N],etot;
inline void Addedge(int u,int v)
{
    next[etot]=ptr[u],ptr[u]=etot,succ[etot++]=v;
}
inline void addedge(int u,int v)
{
    Addedge(u,v),Addedge(v,u);
}
int depth[N],low[N];
bool flag[N];
void tarjan(int node,int ftr)
{
    low[node]=depth[node]=depth[ftr]+1;
    int cnt=0;
    for(int i=ptr[node];i;i=next[i])
        if(succ[i]!=ftr)
            if(depth[succ[i]])low[node]=min(low[node],depth[succ[i]]);
            else
            {
                tarjan(succ[i],node);
                ++cnt;

                low[node]=min(low[node],low[succ[i]]);
                if(low[succ[i]]>=depth[node]&&ftr)flag[node]=0;
            }
    if(ftr==0&&cnt>1)flag[node]=0;

    //printf("---%d,%d---\nflag=%d\n",node,ftr,flag[node]);
}
int belong[N],size[N],cnt[N];
bool con[N];
void floodfill(int node,int col)
{
    ++size[col];
    belong[node]=col;
    for(int i=ptr[node];i;i=next[i])
        if(!belong[succ[i]]&&flag[succ[i]])
            floodfill(succ[i],col);
}
int main()
{
    freopen("bzoj2730.in","r",stdin);
    freopen("bzoj2730.out","w",stdout);
    int n,m;
    int cs=1;
    for(scanf("%d",&m);m;scanf("%d",&m))
    {
        etot=1;
        memset(ptr,0,sizeof(ptr));
        memset(flag,1,sizeof(flag));
        memset(depth,0,sizeof(depth));
        memset(belong,0,sizeof(belong));
        memset(cnt,0,sizeof(cnt));
        memset(size,0,sizeof(size));
        int u,v;
        n=1;
        while(m--)
        {
            scanf("%d%d",&u,&v);
            addedge(u,v);
            n=max(n,max(u,v));
        }
        if(n==1)
        {
            printf("Case %d: 0 1\n",cs++);
            continue;
        }
        for(int i=n;i;--i)
            if(!depth[i])
                tarjan(i,0);
        int ctot=1;
        for(int i=n;i;--i)
            if(!belong[i]&&flag[i])
                floodfill(i,ctot++);
        for(int i=n;i;--i)
            if(!flag[i])
            {
                for(int j=ptr[i];j;j=next[j])
                    if(!con[belong[succ[j]]])
                    {
                        con[belong[succ[j]]]=1;
                        ++cnt[belong[succ[j]]];
                    }
                for(int j=ptr[i];j;j=next[j])con[belong[succ[j]]]=0;
            }
        int Min=0;
        unsigned long long sum=1;
        for(int i=ctot;--i;)
            if(cnt[i]==0)
                if(size[i]>1)
                {
                    Min+=2;
                    sum*=size[i]*(size[i]-1)>>1;
                }
                else ++Min;
            else if(cnt[i]==1)
            {
                ++Min;
                sum*=size[i];
            }
        printf("Case %d: %d %llu\n",cs++,Min,sum);
    }
}

总结:
①割点的度必然≥2.
②极大点联通分量与外界相连的点必然是割点。
③点集S是双连通分量等价于对于任意三个点都有以此经过它们的一条简单路径。
④一定要切记考虑图不连通的情况。
⑤在求割点的时候一个点的low是不能对它祖先的low取min的,应该对它祖先的depth取min;而在求桥的时候是可以的。它们并不相同,写的时候不要盲目跟着习惯,一定要仔细考虑一下。

猜你喜欢

转载自blog.csdn.net/ta201314/article/details/53010637