【转】Cactus 仙人掌图 有向&无向

本文深入探讨了有向Cactus图与无向Cactus图的概念,提供了判别方法及应用实例。通过具体题目的解析,详细解释了如何通过DFS算法判断有向图是否为Cactus图,并介绍了无向Cactus图的定义及求解方法。文章旨在帮助读者理解Cactus图的特性及其在图论中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://blog.youkuaiyun.com/kksleric/article/details/7870398

有向Cactus图:

1.它是一个强连通图。

2.它的任意一条边都属于且仅属于一个环。

有向Cactus图判定:

性质有向CactusDFS树没有横向边(不等价于非父子边)

性质2 low(u)<=dfn(v) (uv的儿子)

性质设某个点va(v)个儿子的low值小于dfn(v),同时v自己有b(v)条逆向边。那么a(v)+b(v)<2

这三条性质也就是一个有向图是有向Cactus的充要条件。详细的证明请看《cactus solution》写的很详细,三个条件都有。

对应的题目HDU 3594,只要搞懂了定理很好实现,通过此题深刻理解了横叉边和反向边的区别。


无向Cactus图定义:

1.它是一个连通图。

2.它的任意一条边都至多属于一个环。

poj 2793 Cactus

题意:判断一个图是否为cactus图,并求计算一个图的cactus度:有多少个生成子图(包括自身)也是cactus。

解法:根据无向仙人掌图的定义,只需判断每条边属于几个环即可,对图进行dfs后所有父子边形成以棵树,每条反向边<a,b>加入树后都会使a-lca[a][b]--b形成一个环,可以用poj3417的做法(详见《Tarjan离线算法求LCA小结》)统计每条边被环覆盖了多少次。还有一种方法是在dfs时顺便维护,实现起来可能还会更简单些。

在确定是cactus图后只需统计每个环上有多少条边,然后利用乘法原理就可以计算度数.由于每条反向边只会形成一个环,因此记录每个节点的深度depth[],在碰到反向边<b,a>时,depth[b]-depth[a]+1就是这个环内边的条数,累乘即可,注意要用高精。

//无向cactus
//POJ 2793
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#define pb push_back
#define mp make_pair
#define eps 1e-9
#define zero(x) (fabs(x)<eps)
#define pi acos(-1.0)
#define f1 first
#define f2 second
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define initial 1,n,1
const int inf=0x3f3f3f3f;
const long long INF=1LL<<50;
using namespace std;
typedef long long LL;
typedef pair <int, int> PII;
template<typename X> inline bool minimize(X&p,X q){if(p<=q)return 0;p=q;return 1;}
template<typename X> inline bool maximize(X&p,X q){if(p>=q)return 0;p=q;return 1;}
#define N 20005
#define M 70010
const int L = 2000;
const int B = 10000;
struct BigInteger {
    BigInteger(int number = 0) : length(1) {
        memset(digit, 0, sizeof(digit));
        digit[0] = number;
    }
    void init(int number)
    {   length=1;digit[0] = number%B;
        if(number/B)
            {
                length++;
                digit[1]=number/B;
            }
    }
    void normalize() {
        while (length && !digit[length - 1]) {
            length --;
        }
    }
    BigInteger operator=(const BigInteger&oth){
        length=oth.length;
        for (int i=0;i<length;i++)
             digit[i]=oth.digit[i];
        return *this;
    }
    int length, digit[L];
};


void mul_(const BigInteger &a, const BigInteger &b,BigInteger &c)
{   memset(c.digit, 0, sizeof(int)*c.length);
    c.length = a.length + b.length;
    for (int i = 0; i < a.length; ++ i) {
        for (int j = 0, delta = 0; j <= b.length; ++ j) {
            delta += a.digit[i] * b.digit[j] + c.digit[i + j];
            c.digit[i + j] = delta % B;
            delta /= B;
        }
    }
    c.normalize();
}

struct edge{
    int from,to;
    edge(int a=0,int b=0):from(a),to(b){}
};
vector<int > g[N],rr;
vector<edge> ed;
BigInteger ans,ttt,tmp;
bool vis[N],pp[N],ok[M];
bool isc;
int hea,scc,tim;
int fa[N];
int n,m;

void add(int x,int y)
{
    ed.pb(edge(x,y));
    ed.pb(edge(y,x));
    int m=ed.size();
    g[x].pb(m-2);
    g[y].pb(m-1);
}

void dfss(int now,int from=-1)
{
    vis[now]=1;
    for (int i=0,v;i<g[now].size();++i)if (!ok[g[now][i]])
        {
            v=ed[g[now][i]].to;
            if (v==from) continue;
            if (vis[v])
                {ok[g[now][i]^1]=1;
                 int t=now;
                 int tt=2;
                 while (t!=v)
                    {   if (pp[t]) {isc=0; if (!isc) return;}
                        pp[t]=1;
                        tt++;
                        t=fa[t];
                    }
                 if (tt>3) rr.pb(tt);
                }
            else {fa[v]=now;
                  dfss(v,now);
                  if (!isc) return ;
                 }
        }
}

void doit()
{
    for (int i=1;i<=n;i++) g[i].clear();
    ed.clear();
    for (int i=1,y,x,z;i<=m;i++)
        {
            scanf("%d",&y);
            scanf("%d",&z);
            for (int i=1;i<=y-1;i++)
            {
                scanf("%d",&x);
                if (ed.size()<M-10)add(x,z);
                z=x;
            }
        }
    if (ed.size()>=M-10){printf("0\n"); return;}
    memset(vis,0,sizeof(vis));
    memset(fa,0,sizeof(fa));
    memset(pp,0,sizeof(pp));
    memset(ok,0,sizeof(ok));
    isc=1;rr.clear();
    for (int i=1;i<=n;i++) if (!vis[i]) {dfss(i,-1);break;}
    if(!isc) {printf("0\n"); return;}
    for (int i=1;i<=n;i++) if (!vis[i]) {printf("0\n"); return;}

    ans=1;
    for (int i=0;i<rr.size();i++)
        {
             ttt.init(rr[i]);
             if (i%2==0) mul_(ans,ttt,tmp);
             else mul_(tmp,ttt,ans);
        }
    if (rr.size()%2==1) ans=tmp;
    printf("%d", ans.digit[ans.length - 1]);
    for (int i = ans.length - 2; i >= 0; -- i) {
            printf("%04d", ans.digit[i]);
        }
    puts("");
}
int main()
{
    while (scanf("%d%d",&n,&m)!=EOF) doit();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值