间谍网络_洛谷1262_强连通分量

题目描述


由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。

我们的反间谍机关提供了一份资料,色括所有已知的受贿的间谍,以及他们愿意收受的具体数额。同时我们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有n个间谍(n不超过3000),每个间谍分别用1到3000的整数来标识。

请根据这份资料,判断我们是否有可能控制全部的间谍,如果可以,求出我们所需要支付的最少资金。否则,输出不能被控制的一个间谍。

输入格式:


第一行只有一个整数n。

第二行是整数p。表示愿意被收买的人数,1≤p≤n。

接下来的p行,每行有两个整数,第一个数是一个愿意被收买的间谍的编号,第二个数表示他将会被收买的数额。这个数额不超过20000。

紧跟着一行只有一个整数r,1≤r≤8000。然后r行,每行两个正整数,表示数对(A, B),A间谍掌握B间谍的证据。

输出格式:


如果可以控制所有间谍,第一行输出YES,并在第二行输出所需要支付的贿金最小值。否则输出NO,并在第二行输出不能控制的间谍中,编号最小的间谍编号。

题解


首先想到如果是NO的话那么贿赂所有能贿赂的人都不能控制所有间谍,直接DFS
由于可能出现环,那么一个环里的所有间谍只需要贿赂一个最便宜的就好了,理所当然缩点
缩点之后就是一个DAG了,类似很多条链。对于同一条链上的间谍,显然dfs序先的点更优(显然),那么那些入度为0的点优先级就最高了。选上所有入度为0的点就是YES的花费

一道水题调n久也是醉了

Code


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug puts("-----")
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define drp(i, st, ed) for (int i = st; i >= ed; i -= 1)
#define fill(x, t) memset(x, t, sizeof(x))
#define min(x, y) x<y?x:y
#define max(x, y) x>y?x:y
#define PI (acos(-1.0))
#define EPS (1e-8)
#define INF (1<<30)
#define ll long long
#define db double
#define ld long double
#define N 5001
#define E N * 8 + 1
#define L 255
using namespace std;
struct edge{int x, y, w, next;}e[E], g[E];
int ind[N], dfn[N], low[N], vis[N], lsE[N], lsG[N], edgeCntE, edgeCntG;
int totCost[N], inStack[N], memb[N], cost[N], comp[N], tot, cnt;
stack<int>s;
inline int read(){
    int x = 0, v = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9'){
        if (ch == '-'){
            v = -1;
        }
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0'){
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return x * v;
}
inline int addEdgeE(int x, int y, int w = 0){
    e[++edgeCntE] = (edge){x, y, w, lsE[x]}; lsE[x] = edgeCntE;
    return 0;
}
inline int addEdgeG(int x, int y, int w = 0){
    g[++edgeCntG] = (edge){x, y, w, lsG[x]}; lsG[x] = edgeCntG;
    return 0;
}
inline int dfs1(int now){
    cnt += 1;
    dfn[now] = low[now] = cnt;
    inStack[now] = 1;
    s.push(now);
    for (int i = lsE[now]; i; i = e[i].next){
        if (!dfn[e[i].y]){
            dfs1(e[i].y);
            low[now] = min(low[now], low[e[i].y]);
        }else{
            if (inStack[e[i].y]){
                low[now] = min(low[now], dfn[e[i].y]);
            }
        }
    }
    if (dfn[now] == low[now]){
        tot += 1;
        for (int tmp = 0; tmp != now; ){
            tmp = s.top(); s.pop();
            comp[tmp] = tot;
            inStack[tmp] = 0;
            if (cost[tmp] != 0){
                totCost[tot] = min(totCost[tot], cost[tmp]);
            }
        }
    }
}
inline int tarjan(int n){
    fill(totCost, 63);
    fill(inStack, 0);
    fill(dfn, 0);
    fill(low, 0);
    tot = cnt = 0;
    rep(i, 1, n){
        if (!dfn[i]){
            dfs1(i);
        }
    }
}
inline int dfs2(int now){
    vis[now] = 1;
    for (int i = lsG[now]; i; i = g[i].next){
        dfs2(g[i].y);
    }
}
int main(void){
    int n = read(), p = read();
    edgeCntE = edgeCntG = 1;
    rep(i, 1, p){
        memb[i] = read();
        cost[memb[i]] = read();
    }
    int m = read();
    rep(i, 1, m){
        int x = read(), y = read();
        addEdgeE(x, y);
    }
    tarjan(n);
    fill(ind, 0);
    rep(i, 2, edgeCntE){
        if (comp[e[i].x] != comp[e[i].y]){
            addEdgeG(comp[e[i].x], comp[e[i].y]);
            ind[comp[e[i].y]] += 1;
        }
    }
    int ans = 0;
    rep(i, 1, p){
        if (!vis[comp[memb[i]]]){
            dfs2(comp[memb[i]]);
        }
    }
    rep(i, 1, n){
        if (!vis[comp[i]]){
            printf("NO\n%d\n", i);
            return 0;
        }
    }
    rep(i, 1, tot){
        if (!ind[i]){
            ans += totCost[i];
        }
    }
    printf("YES\n%d\n", ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值