[Luogu] P1262 间谍网络

\(Link\)

Description

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

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

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

Solution

还是有一些东西要考虑的。

首先肯定要跑一遍缩点,然后在同一强连通分量内的间谍就只要收买一个了。那么我们肯定是要把所有入度为零的点(缩点后)买掉,如果这一强连通分量内的所有间谍都不能被收买,那么就不能收买所有间谍,输出这个强连通分量内的最小间谍编号。否则我们肯定是收买入度为零的点中金额最小的那个间谍。

Code

#include <bits/stdc++.h>

using namespace std;

int n, m, p, tot, top, ind, cnt, fl = 1, id, sum, mnid[3005], mn[3005], col[3005], ok[3005], money[3005], rd[3005], hd[3005], to[8005], nxt[8005], dfn[3005], low[3005], que[3005], vis[3005];

int read()
{
	int x = 0, fl = 1; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * fl;
}

void add(int u, int v)
{
	tot ++ ;
	to[tot] = v;
	nxt[tot] = hd[u];
	hd[u] = tot;
	return;
}

void dfs(int x)
{
	low[x] = dfn[x] = ++ ind;
	que[ ++ top] = x;
	vis[x] = 1;
	for (int i = hd[x]; i; i = nxt[i])
	{
		int y = to[i];
		if (!dfn[y])
		{
			dfs(y);
			low[x] = min(low[x], low[y]);
		}
		else if (vis[y]) low[x] = min(low[x], dfn[y]);
	}
	if (low[x] == dfn[x])
	{
		cnt ++ ;
		int now = -1;
		do
		{
			now = que[top --];
			col[now] = cnt;
			if (money[now]) mn[cnt] = min(mn[cnt], money[now]);
			ok[cnt] |= money[now];
			mnid[cnt] = min(mnid[cnt], now);
			vis[now] = 0;
		} while (now != x);
	}
	return;
}

int main()
{
	n = read(); p = read();
	for (int i = 1; i <= p; i ++ )
	{
		int x = read();
		money[x] = read();
	}
	m = read();
	for (int i = 1; i <= m; i ++ )
	{
		int u = read(), v = read();
		add(u, v);
	}
	memset(mn, 0x3f, sizeof(mn));
	memset(mnid, 0x3f, sizeof(mnid));
	for (int i = 1; i <= n; i ++ )
		if (!dfn[i])
			dfs(i);
	for (int x = 1; x <= n; x ++ )
	{
		for (int i = hd[x]; i; i = nxt[i])
		{
			int y = to[i];
			if (col[y] != col[x]) rd[col[y]] ++ ;		
		}
	}
	for (int i = 1; i <= cnt; i ++ )
	{
		if (!rd[i])
		{
			if (!ok[i])
			{
				fl = 0;
				id = mnid[i];
				break;
			}
			else sum += mn[i];
		}
	}
	if (fl)
	{
		puts("YES");
		printf("%d\n", sum);
	}
	else
	{
		puts("NO");
		printf("%d\n", id);
	}
	return 0;	
}
posted @ 2020-11-02 13:34  andysj  阅读(70)  评论(0编辑  收藏  举报