洛谷 P4129 [SHOI2006]仙人掌

题意:

给出一个无向图,在判断原图是否为一个仙人掌图的基础上,求仙人掌的支撑子图个数
支撑子图:去掉一些边,但并不改变连通性,这样得到的子图为支撑子图。

题解:

1个仙人掌 <=> 连通性&点双均为简单环。
于是先dfs一次检验连通性,然后tarjan求点双,检验点双的点数=边数。

Ans=(+1) A n s = ∏ ( 环 长 + 1 )

因为树边不可删除,每一个环上可以选择不删,也可以选择删除一条边,两条以上则破坏连通性。
需要使用高精度姿势,且标答数据错误。。。有前导0。。。但疼的一批。
Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e4+100;
const int maxm = 1e6+100;
int first[maxn],des[maxm*2],nxt[maxm*2],tot;
int m,n;
int dfn[maxn],low[maxn],dfs_clock;
bool vis[maxn];
int bcc_cnt,bcc_no[maxn];
int st[maxm],top;
struct Big_Number{
    LL a[1000],len;
    const LL bas = 100000000;
    void init(){
        len=1;
        memset(a,0,sizeof a);
        a[0]=1;
    }
    void mul(LL val){
        for (int i=0;i<len;i++){
            a[i] = a[i]*val;
        }
        for (int i=0;i<len;i++){
            a[i+1] += a[i]/bas;
            a[i]%=bas;
        }
        while (a[len]){
            a[len+1] = a[len]/bas;
            a[len]%=bas;
            len++;
        }
    }
    void print(){
        printf("%lld",a[len-1]);
        for (int i=len-2;i>=0;i--){
            printf("%.8lld",a[i]);
        }
        printf("\n");
    }
}ans;
inline void addEdge(int x,int y){
    tot++;
    des[tot] =y;
    nxt[tot] = first[x];
    first[x] = tot;
}
void input(){
    scanf("%d%d",&n,&m);
    for (int i=0;i<m;i++){
        int u,k;
        scanf("%d%d",&k,&u);
        for (int j=1;j<k;j++){
            int v;
            scanf("%d",&v);
            addEdge(u,v);
            addEdge(v,u);
            u =v;
        }
    }
}
void dfs(int u){
    vis[u] =1;
    for (int t = first[u];t;t=nxt[t]){
        int v = des[t];
        if (vis[v])continue;
        dfs(v);
    }
}
bool dfs(int node,int fa,int Eid){
 //   cout<<"dfs: "<<node<<" "<<fa<<" "<<endl;
    dfn[node] = low[node] = ++dfs_clock;
    st[top++] = Eid;
    for (int t = first[node];t;t=nxt[t]){
        int v = des[t];
        if (v==fa)continue;
        if (!dfn[v]){
            if (!dfs(v,node,t))return false;
            low[node] = min(low[node],low[v]);
            if (low[v]==dfn[node]){
          //      cout<<"find bcc:"<<node<<" "<<v<<endl;
                bcc_cnt++;
                LL e_cnt=1;
                bool ok = true;
                while (true){
                    int tt = st[--top];
                    if (bcc_no[des[tt]]!=bcc_cnt){
                        bcc_no[des[tt]] = bcc_cnt;
                        e_cnt++;
                    }else{
                        ok =false;
                    }
                    if (tt==t)break;
                }
                if (ok){
                    ans.mul(e_cnt);
                }else{
                    return false;
                }
            }
        }else if (dfn[v]<dfn[node]){
            low[node] = min(low[node],dfn[v]);
            st[top++] = t;
        }
    }
    if (st[top-1]==Eid)top--;
    return true;
}
void solve(){
    dfs(1);
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            cout<<0<<endl;
            return;
        }
    }
    ans.init();
    if (dfs(1,-1,-1)){
        ans.print();
    }else{
        cout<<0<<endl;
    }
}
int main(){
    #ifdef debug
    freopen("input.in","r",stdin);
    #endif // debug
    input();
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值