POJ 2186 Popular Cows(强连通,kosaraju算法)

本文介绍了如何通过双向深度优先搜索(DFS)算法解决有向图中满足特定性质的点的计数问题。具体而言,算法首先通过DFS找到所有强连通分量(SCC),然后利用反图进行进一步验证,最终确定满足条件的点数量。

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

题意:

有m个有序对,且满足传递性。求解满足以下性质的点的个数,从其他每个点都可以通过有向边到达该点。

思路:

如果A, B都满足性质,则A,B至少在一个有向环上,所以满足性质的点属于同一连通分量。

我们可以用双向dfs算法求出SCC,并且从最后一个连通分量拿一个元素来检查是否可以沿着反图访问到每个点。

//#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <climits>
#include <set>
#include <map>

using namespace std;

#define SPEED_UP iostream::sync_with_stdio(false);
#define FIXED_FLOAT cout.setf(ios::fixed, ios::floatfield);
#define rep(i, s, t) for(int (i)=(s);(i)<=(t);++(i))
#define urep(i, s, t) for(int (i)=(s);(i)>=(t);--(i))
#define in_bound(l, r, i) (l)<=(i)&&(i)<(r)
#define pb push_back

typedef long long LL;

struct Edge{
    int from, to;
};

const int Maxn = 10000+100;
const int inf = INT_MAX/2;

int used[Maxn], sccNo[Maxn], n, m;
vector<int> g[Maxn], rg[Maxn], vs;

void dfs(int now) {
    used[now] = 1;
    int sz = g[now].size(), v;
    rep(i, 0, sz-1) {
        v = g[now][i];
        if (!used[v]) dfs(v);
    }
    vs.push_back(now);
}

void rdfs(int now, int flag) {
    used[now]  = 1;
    sccNo[now] = flag;
    int sz = rg[now].size(), v;
    rep(i, 0, sz-1) {
        v = rg[now][i];
        if (!used[v]) rdfs(v, flag);
    }
}

int scc(int V) {
    memset(used, 0, sizeof(used));
    vs.clear();
    //rep(i, 0, V-1)
    //    if (!used[i]) dfs(i);

    urep(i, V-1, 0)
        if (!used[i]) dfs(i);

    memset(used, 0, sizeof(used));
    int cnt = 0;
    urep(i, V-1, 0)
        if (!used[vs[i]]) rdfs(vs[i], cnt++);
    return cnt;
}

void add_edge(int from, int to) {
    g[from].push_back(to);
    rg[to].push_back(from);
}

void init() {
    rep(i, 0, n-1) {
        g[i].clear();
        rg[i].clear();
    }
    rep(i, 0, m-1) {
        int x, y;
        cin >> x >> y;
        add_edge(x-1, y-1);
    }
}

int solve() {
    int tot = scc(n);
    int num = 0, u;
    rep(i, 0, n-1)
        if (sccNo[i] == tot-1) {
            ++num;
            u = i;
        }
    memset(used, 0, sizeof(used));
    rdfs(u, tot-1);
    rep(i, 0, n-1)
        if (!used[i]) return 0;
    return num;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.in", "r", stdin);
#endif
    SPEED_UP
    while (cin >> n >> m) {
        init();
        cout << solve() << endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值