UVA 1361 Cactus

        这题是我做的最艰难的几道题之一吧……uva上只有9个人过,不过poj上也有这题,那个有193人过,这也不少了啊,为毛没有题解呢?唉,这题做了好久,由于想不出好的方法,所以用的方法比较渣,看代码长度的话,我的在poj上估计能排前几ORZ,实在是太弱了啊,如果有好的方法希望各路大牛能指点指点啊,感激不尽……

 

题意:给定一个无向图,求无向图的“仙人掌度”,即它有多少生成子图(包括自己)也是仙人掌。仙人掌定义是每条边最多在一个简单回路上的连通无向图。

思路:生成子图要求顶点集不变,边集为原图边集的子集,原图(已经确定是仙人掌)的边有两种,一种是桥,另一种是恰好在一个圈中的边,注意到桥是不能删除的,那么只能删除圈中的边,但是对于每个圈,最多只能删除其中一条边,否则就会使图不连通,这样的话,如果一个圈中的边的数量是L的话,那么可选的删边方案就是L+1,最后答案就是所有(Li+1)的乘积。

          接下来的问题是如何判断一个图是否是仙人掌以及如何确定Li。这个问题我没想到什么好的解决方案,我是这么做的,先找图的双连通分量,然后判断每个双连通分量是否有欧拉回路,如果没有,那么肯定不是仙人掌图,如果有欧拉回路,那么对于点双连通分量来说有两种情况,一种是每个点连通分量形成一个简单环,另一种就是有重边的环了,这两种情况只要判断点数和边数是否相等就行了。

         另外需要注意的一些问题:整个图需要连通的,所以开始的时候要判断一下是否是连通图,输出的数据会非常大,需要大整数,长度大概在6000左右吧,我开的7000才过了,长度短会RE……

 

代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 2139062143
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=20000+10;
//大整数,写的比较挫
struct BigInt
{
    int num[7000];
    int len;
    void clear()
    {
        for(int i=0;i<7000;++i) num[i]=0;
        len=0;
    }
    void getBigInt(ll n)
    {
        clear();
        if(n==0) num[len++]=0;
        while(n!=0)
        {
            num[len++]=n%10;
            n/=10;
        }
    }
    void getBigInt(int n)
    {
        ll m=(ll)n;
        getBigInt(m);
    }
    BigInt operator * (const BigInt a) const
    {
        int m=a.len;
        int n=len;
        BigInt c;
        c.clear();
        int bs=0,w=0,jinwei=0,tp;
        for(int i=0;i<m;++i)
        {
            jinwei=0;
            w=0;
            for(int j=0;j<n;++j)
            {
                tp=a.num[i]*num[j]+jinwei+c.num[bs+w];
                c.num[bs+w]=tp%10;
                jinwei=tp/10;
                w++;
            }
            while(jinwei!=0)
            {
                c.num[bs+w]=jinwei%10;
                jinwei/=10;
                w++;
            }
            c.len=max(c.len,bs+w);
            bs++;
        }
        return c;
    }
};
struct Edge
{
    int u,v;
};
//一大堆数组……
vector<int>G[maxn];
int pre[maxn],ebcno[maxn],bccno[maxn],dfs_clock,ebc_cnt,bcc_cnt;
int parents[maxn],sum;
int pnum[maxn],bnum[maxn],degree[maxn];
ll tmp[maxn];
int top;
BigInt cnt;
stack<int>S;
stack<Edge>SS;
//并查集,用来判图是否连通
int Find(int x)
{
    return x==parents[x]?parents[x]:parents[x]=Find(parents[x]);
}
void Uion(int x,int y)
{
    int a=Find(x);
    int b=Find(y);
    if(a!=b)
    {
        sum++;
        parents[b]=a;
    }
}
//求边连通分量
int dfs(int u,int fa)
{
    int lowu=pre[u]=++dfs_clock;
    S.push(u);
    for(int i=0;i<G[u].size();++i)
    {
        int v=G[u][i];
        if(!pre[v])
        {
            int lowv=dfs(v,u);
            lowu=min(lowu,lowv);
        }
        else if(pre[v]<pre[u]&&v!=fa)
        {
            lowu=min(lowu,pre[v]);
        }
    }
    if(lowu==pre[u])
    {
        ebc_cnt++;
        while(true)
        {
            int x=S.top();S.pop();
            ebcno[x]=ebc_cnt;
            bnum[ebc_cnt]=x;
            if(x==u) break;
        }
    }
    return lowu;
}
void find_ebc(int n)
{
    memset(pre,0,sizeof(pre));
    memset(ebcno,0,sizeof(ebcno));
    while(!S.empty()) S.pop();
    dfs_clock=ebc_cnt=0;
    for(int i=1;i<=n;++i)
       if(!pre[i]) dfs(i,-1);
}
//求点连通分量,将每个点连通分量中边的个数存到数组中
int dfs2(int u,int fa,int ebcnum)
{
    int lowu=pre[u]=++dfs_clock;
    for(int i=0;i<G[u].size();++i)
    {
        int v=G[u][i];
        if(ebcno[v]!=ebcnum) continue;
        Edge e=(Edge){u,v};
        if(!pre[v])
        {
            SS.push(e);
            //sst[top++]=e;
            int lowv=dfs2(v,u,ebcnum);
            lowu=min(lowu,lowv);
            if(lowv>=pre[u])
            {
                bcc_cnt++;
                int sum=0,bs=0;
                while(true)
                {
                    Edge x=SS.top();SS.pop();
                    bs++;
                    if(bccno[x.u]!=bcc_cnt){sum++;bccno[x.u]=bcc_cnt;}
                    if(bccno[x.v]!=bcc_cnt){sum++;bccno[x.v]=bcc_cnt;}
                    if(x.u==u&&x.v==v)
                    {
                        if(sum!=bs) tmp[top++]=0;   //如果边和点的数量不相等,说明不是仙人掌图
                        else
                        tmp[top++]=sum+1;
                        break;
                    }
                }
            }
        }
        else if(pre[v]<pre[u]&&v!=fa)
        {
            SS.push(e);
            lowu=min(lowu,pre[v]);
        }
    }
    return lowu;
}
void find_bcc(int u,int ebcnum)
{
    top=0;
    cnt.getBigInt(1);
    dfs2(u,-1,ebcnum);
    BigInt temp;
    for(int i=0;i<top;++i)
    {
        temp.getBigInt(tmp[i]);
        cnt=cnt*temp;
    }
}
BigInt slove(int n)
{
    find_ebc(n);
    memset(pnum,0,sizeof(pnum));
    memset(degree,0,sizeof(degree));
    for(int u=1;u<=n;++u)
    {
        for(int j=0;j<G[u].size();++j)
        {
            int v=G[u][j];
            if(ebcno[u]==ebcno[v])
            {
                degree[u]++;
                degree[v]++;
            }
        }
    }
    BigInt res;
    res.getBigInt(0);
    for(int u=1;u<=n;++u)
    {
        for(int j=0;j<G[u].size();++j)
        {
            int v=G[u][j];
            if(ebcno[u]==ebcno[v])
            {
                if((degree[u]/2)%2) return res;
                if((degree[v]/2)%2) return res;
            }
        }
    }
    res.getBigInt(1);
    for(int i=1;i<=n;++i)
        pnum[ebcno[i]]++;
    memset(pre,0,sizeof(pre));
    memset(bccno,0,sizeof(bccno));
    dfs_clock=bcc_cnt=0;
    for(int i=1;i<=ebc_cnt;++i)
    {
        if(pnum[i]>=3)
        {
            find_bcc(bnum[i],i);
            res=res*cnt;
        }
    }
    return res;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,m,k,tcase=0;
    while(~scanf("%d%d",&n,&m))
    {
        if(tcase++) printf("\n");
        int a,b;
        for(int i=0;i<=n;++i)
        {
            parents[i]=i;
            G[i].clear();
        }
        sum=0;
        while(m--)
        {
            scanf("%d",&k);
            scanf("%d",&a);
            for(int i=1;i<k;++i)
            {
                scanf("%d",&b);
                G[a].push_back(b);
                G[b].push_back(a);
                Uion(a,b);
                a=b;
            }
        }
        BigInt ans;
        if(sum+1!=n) ans.getBigInt(0);
        else ans=slove(n);
        for(int i=ans.len-1;i>=0;--i)
          printf("%d",ans.num[i]);
        printf("\n");
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值