洛谷 P4129 [SHOI2006]仙人掌(无向仙人掌判断求环)

题目链接
题意

判断一个无向图是否为仙人掌图

  • 强连通
  • 一条边最多属于一个环

任意删边,求删边后图还是强连通(支撑子图)方案数

思路

如果这个图是仙人掌,帮所有环的边数+1相乘即可。
大数偷懒直接粘的别人代码(^_^)


据说数据弱代码可能有问题,又用点双写了一次,点双要求连通分量中边数等于点数,注意不要把只有两个点的加入判断(我这代码会帮割点和他儿子加入)

代码
#include <bits/stdc++.h>
using namespace std;

#define ll long long

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;

const int N = 20005;
vector<int> e[N];
int sum[N], dep[N], d[N], flag, f[N];

void dfs(int u, int fa) {
    dep[u] = dep[fa] + 1;
    f[u] = fa;
    for(auto v : e[u]) {
        if(v == fa) continue;
        if(!dep[v]) dfs(v, u);
        else if(dep[v] < dep[u]){
            sum[u] = dep[u]-dep[v]+1;
            for(int i = u; i != v; i = f[i]) { // 点可以在多个环中,不要写成 i!=f[v] wa了半天
                ++d[i];
                if(d[i] > 1) flag = 1;
            }
        }
    }
}

int main() {
    int n, m;
    scanf("%d%d",&n,&m);
    while(m--) {
        int tmp, u, v;
        scanf("%d%d",&tmp,&u);
        while(--tmp) {
            scanf("%d",&v);
            e[u].push_back(v);
            e[v].push_back(u);
            u = v;
        }
    }
    dfs(1,0);
    for(int i = 1; i <= n; ++i) if(dep[i] == 0) flag = 1;
    if(flag) return !printf("0\n");
    ans.init();
    for(int i = 1; i <= n; ++i) ans.mul(sum[i]+1ll);
    ans.print();
    return 0;
}
点双代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long

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;

const int N = 20005;
vector<int> e[N], ds[N];
int low[N], dfn[N], tot, ds_cnt, book[N];
stack<int> st;

void tarjan(int u, int fa) {
    low[u] = dfn[u] = ++tot;
    st.push(u);
    for(auto v : e[u]) {
        if(v == fa) continue;
        if(!dfn[v]) {
            tarjan(v,u);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u]) {
                ++ds_cnt;
                while(1) {
                    int now = st.top();
//                        printf("%d ",now);
                    st.pop();
                    ds[ds_cnt].push_back(now);
                    if(now == v) break;
                }
//                printf("%d -- %d\n",u,ds_cnt);
                ds[ds_cnt].push_back(u);
            }
        }
        else low[u] = min(low[u], dfn[v]);
    }
}

int main() {
    int n, m;
    scanf("%d%d",&n,&m);
    while(m--) {
        int tmp, u, v;
        scanf("%d%d",&tmp,&u);
        while(--tmp) {
            scanf("%d",&v);
            e[u].push_back(v);
            e[v].push_back(u);
            u = v;
        }
    }
    ans.init();
    tarjan(1,1);
    if(tot != n) return !printf("0\n");
    for(int i = 1; i <= ds_cnt; ++i) {
        int cnt = 0;
        for(auto u : ds[i]) book[u] = 1;
        for(auto u : ds[i]) for(auto v : e[u]) if(book[v]) ++cnt;
        for(auto u : ds[i]) book[u] = 0;
//        printf("%d %d\n",cnt,ds[i].size());
        if(cnt/2 > ds[i].size()) return !printf("0\n");
        if(cnt/2 == ds[i].size()) ans.mul(cnt/2+1);
    }
    ans.print();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值