题解 P1262 间谍网络

题目描述

Link

给定一张 \(n\) 个点 \(m\) 条边的有向图,有一些点可以花 \(w_i\) 的代价控制,如果一个点被控制,那么它能到达的所有点都能被控制。

求能否把所有的点都控制:如果可以,输出控制所有点的最小代价;如果不能,输出不能控制的编号最小的点的编号。

\(1 \leq n \leq 10^3 ,1 \leq m \leq 8 \times 10^3 ,1 \leq w_i \leq 2 \times 10^4\)

Solution

首先考虑什么情况下不能把所有点控制:有点不能控制,等价于这个点不能用 \(w_i\) 的代价控制,也不能通过其他的点间接控制。

那么直接把所有能用 \(w_i\) 的代价控制的点能到达的点标记一下(当然要把自己也标记),如果有点没有被标记,那么这个点就不能被控制。

然后考虑怎么求最小代价:

如果给的图是一个有向无环图,那么如果要控制所有点,所有入度是 \(0\) 的点一定要花费代价进行控制。

这些入度为 \(0\) 的所有点能够控制整张网络的点,而所有的 \(w_i\) 都是正数,我们应该尽量少选,所以选所有入度为 \(0\) 的点就足够了。

但是这题不保证是有向无环图,我们可以用 tarjan 缩点,然后直接在缩完点的图 \(G'\) 进行如上的操作就可以了。

至于 tarjan 缩点之后点的权值嘛,因为任意选里面的一个点都能控制整个点,所以选权值最小的点就可以了。

时间复杂度 \(\mathcal{O}(n + m)\) ,代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int N = 3005 ,M = 8005 ,INF = 0x3f3f3f3f;
inline int read() {
    int num = 0 ,f = 1; char c = getchar();
    while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
    while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
    return num * f;
}
struct Edge {
    int to ,next;
    Edge (int to = 0 ,int next = 0) : to(to) ,next(next) {}
}G[M]; int head[N] ,cnt;
inline void add(int u ,int v) {
    G[++cnt] = Edge(v ,head[u]); head[u] = cnt;
}
int s[N] ,col[N] ,id[N] ,low[N] ,dfn ,tot ,top ,a[N] ,w[N] ,ids[N];
inline void tarjan(int now) {
    id[now] = low[now] = ++dfn;
    s[++top] = now;
    for (int i = head[now]; i ; i = G[i].next) {
        int v = G[i].to;
        if (!id[v]) {
            tarjan(v);
            low[now] = min(low[now] ,low[v]);
        }
        else if (!col[v]) low[now] = min(low[now] ,id[v]);
    }
    if (low[now] == id[now]) {
        col[now] = ++tot; w[tot] = a[now]; ids[tot] = now;
        while (s[top] != now) {
            col[s[top]] = tot;
            ids[tot] = min(ids[tot] ,s[top]);
            w[tot] = min(w[tot] ,a[s[top--]]);
        }
        top--;
    }
}
int n ,m ,rd[N] ,res;
signed main() {
    memset(a ,0x3f ,sizeof(a));
    n = read(); m = read();
    while (m--) {
        int id = read();
        a[id] = read();
    }
    m = read();
    while (m--) {
        int u = read() ,v = read();
        add(u ,v);
    }
    for (int i = 1; i <= n; i++) if (!id[i] && a[i] != INF) tarjan(i);
    for (int i = 1; i <= n; i++)
        if (!col[i]) return printf("NO\n%d\n" ,i) ,0;
    for (int i = 1; i <= n; i++)
        for (int j = head[i]; j ; j = G[j].next) {
            int v = G[j].to;
            if (col[i] != col[v]) rd[col[v]]++;
        }
    for (int i = 1; i <= tot; i++) if (rd[i] == 0) res += w[i];
    printf("YES\n%d\n" ,res);
    return 0;
}
posted @ 2021-04-21 20:04  recollector  阅读(47)  评论(0编辑  收藏  举报