P1262 间谍网络(Tarjian缩点)

原题链接https://www.luogu.com.cn/problem/P1262icon-default.png?t=LA92https://www.luogu.com.cn/problem/P1262

Tarjian原理icon-default.png?t=LA92https://www.cnblogs.com/shadowland/p/5872257.html

 ACCODE

#include <bits/stdc++.h>
using namespace std;
#define maxn 10010
#define inf 0x3f3f3f3f

/*
 @time表示tarjian的时间戳,即深搜的次序编号
 @cnt表示强连通分量的个数(维护)
 @buy[i]表示可以被收买的间谍,i表示间谍的编号,buy[i]表示收买金额
 @dn[i]表示tarjian算法编号为i的间谍的时间戳,即第几个被访问
 @low[i]表示tarjian算法中编号为i的间谍,能够去到的所有点中时间戳最小的
 # explanation: scc - strongly connected component 强连通分量
 @scc_min_id[i]表示第i个强连通分量中最小的编号
 @scc_min_cost[i]表示第i个强连通分量中,收买金额最小的间谍
 @k[i]表示i号间谍在哪个scc中
 @scc_ru[i]表示第i个强连通分量(缩点后)有没有入度
 @vis[i]表示i号间谍有没有在栈里
 @connect_scc[i][j]表示i号scc和j号scc有没有连边
*/
int n, p, r, timee, cnt;
int buy[maxn], dn[maxn], low[maxn];
int scc_min_id[maxn], scc_min_cost[maxn], k[maxn];
bool vis[maxn], connect_scc[maxn][maxn], scc_ru[maxn];
vector<int> edge[maxn];
stack<int> s;

void tarjian(int x)
{
    low[x] = dn[x] = ++timee;
    s.push(x);
    vis[x] = true;
    for (int i = 0; i < edge[x].size(); i++)
    {
        int y = edge[x][i];
        if (!dn[y])
        {
            tarjian(y);
            low[x] = min(low[x], low[y]);
        }
        else if (vis[y])
        {
            low[x] = min(low[x], low[y]);
        }
    }

    if (dn[x] == low[x])
    {
        int min_cost = inf, id = inf, t;
        cnt++;
        while (1)
        {
            t = s.top();
            s.pop();
            vis[t] = false;
            min_cost = min(min_cost, buy[t]);
            id = min(id, t);
            k[t] = cnt;
            if (t == x)
                break;
        }
        scc_min_cost[cnt] = min_cost;
        scc_min_id[cnt] = id;
    }
}

int main()
{
    cin >> n >> p;
    for (int i = 1; i <= n; i++)
        buy[i] = inf;
    for (int k = 1; k <= p; k++)
    {
        int i, j;
        cin >> i >> j;
        buy[i] = j;
    }
    cin >> r;
    for (int k = 1; k <= r; k++)
    {
        int i, j;
        cin >> i >> j;
        edge[i].push_back(j);
    }
    for (int i = 1; i <= n; i++)
        if (!dn[i])
            tarjian(i);
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < edge[i].size(); j++)
        {
            int m = edge[i][j];
            //如果这条边上两个点都在一个scc上,或者在不同的scc上但是已经判断连通了就continue
            if (k[i] == k[m] || connect_scc[k[i]][k[m]])
                continue;
            connect_scc[k[i]][k[m]] = true;
            //如果i编号所在的scc没有入度,且没有人能收买,那么这个scc相当于孤立了,那么与它连通的scc也相当于没有入度
            if (scc_ru[k[i]] || scc_min_cost[k[i]] != inf)
                scc_ru[k[m]] = true;
        }

    int id = inf, ans = 0;
    bool tag = false;
    //遍历scc
    for (int i = 1; i <= cnt; i++)
    {
        //有入度的scc表示符合
        if (scc_ru[i])
            continue;               
        if (scc_min_cost[i] == inf) //这个强联通分量不能买 说明整张图就无解了
        {
            tag = true;
            id = min(id, scc_min_id[i]); // 找到最小编号
        }
        if (!tag)
            ans += scc_min_cost[i];
    }
    if (tag)
        cout << "NO" << endl
             << id;
    else
        cout << "YES" << endl
             << ans;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linengcs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值