洛谷 P1407 [国家集训队] 稳定婚姻(Tarjan强连通分量,无向图定向)

题目链接

https://www.luogu.com.cn/problem/P1407

思路

观察下图,黑色的边为夫妻关系,红色的边为情人关系。
在这里插入图片描述
容易发现,上图的婚姻关系是不稳定的。因此,处在一个环中的夫妻是可以更换的。

对于求环,我们可以使用Tarjan强连通分量。但Tarjan强连通分量是基于有向图的,而我们构造的确实一个无向图。

我们可以尝试给无向图定向。夫妻之间: G − > B G -> B G>B,情人之间: B − > G B->G B>G

Tarjan求强连通分量,如果一对夫妻在同一个强连通分量中,则婚姻是不安全的,否则就是安全的。

代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define double long double

typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;

const int N = 8e3 + 5, M = 6e4 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;

int n, m, cnt;
int dfn[N], low[N], tim;
int id[N], scc_cnt;
vector<int> idd[N];
bool in_st[N];
stack<int> st;
vector<int> G[N];
map<string, int>mp;
void tarjan(int u)
{
    dfn[u] = low[u] = ++tim;
    st.push(u);
    in_st[u] = true;
    for (int i = 0; i < G[u].size(); i++)
    {
        int j = G[u][i];
        if (!dfn[j])
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if (in_st[j])
            low[u] = min(low[u], low[j]);
    }
    if (dfn[u] == low[u])
    {
        int k;
        scc_cnt++;
        do
        {
            k = st.top();
            st.pop();
            in_st[k] = false;
            id[k] = scc_cnt;
            idd[scc_cnt].push_back(k);
        } while (k != u);
    }
}
void solve(int test_case)
{
    cin >> n;
    string s1, s2;
    for (int i = 1; i <= n; i++)
    {
        cin >> s1 >> s2;
        mp[s1] = i, mp[s2] = n + i;
        G[i].push_back(n + i);
    }
    cin >> m;
    for (int i = 1; i <= m; i++)
    {
        cin >> s1 >> s2;
        G[mp[s2]].push_back(mp[s1]);
    }
    for (int i = 1; i <= n * 2; i++)
    {
        if (!dfn[i])
            tarjan(i);
    }
    for (int i = 1; i <= n; i++)
    {
        if (id[i] == id[n + i])
        {
            cout << "Unsafe" << endl;
        }
        else cout << "Safe" << endl;
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int test = 1;
    // cin >> test;
    for (int i = 1; i <= test; i++)
    {
        solve(i);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值