Luogu P1262 间谍网络

Description:

由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。
我们的反间谍机关提供了一份资料,包括所有已知的受贿的间谍,以及他们愿意收受的具体数额。同时我们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有n个间谍(n不超过3000),每个间谍分别用1到3000的整数来标识。
请根据这份资料,判断我们是否有可能控制全部的间谍,如果可以,求出我们所需要支付的最少资金。否则,输出不能被控制的一个间谍。

Analysis:

显然强连通分量里的点都可以互相控制。
tarjan缩点建新图,存下每个强连通里的点的最小值,同时该强连通分量的费用等于所有点的费用和。
难得做一道题这么顺利就AC了

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define YES 1
#define NO 0
using namespace std;
const int N = 3010,M = 8010,INF = 0x3f3f3f3f;
struct edge{
	int u,v,next;
}e[M],G[M];
int head[N],dfn[N],low[N],scc[N],pr[N],ind[N],num_dfs,num_scc,num_edge1,num_edge2,n,p,m;
int st[N],top;
int Ins[N],cost[N],ans,minimum;
void tarjan(int u)
{
	dfn[u] = low[u] = ++num_dfs;
	st[++top] = u;
	for(int i = head[u];i;i = e[i].next)
	{
		int v = e[i].v;
		if(!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u],low[v]);
		}else if(!scc[v])
		{
			low[u] = min(low[u],dfn[v]);
		}
	}
	if(low[u] == dfn[u])
	{
		scc[u] = ++num_scc;
		Ins[num_scc] = u;
		if(pr[u]) cost[num_scc] = min(cost[num_scc],pr[u]);
		while(st[top] != u)
		{
			scc[st[top]] = num_scc;
			Ins[num_scc] = min(st[top],Ins[num_scc]);
			if(pr[st[top]]) cost[num_scc] = min(cost[num_scc],pr[st[top]]);
			--top;
		}
		--top;
	}
}
void add(edge E[],int a,int b,int &num)
{
	E[++num].next = head[a];
	E[num].u = a;
	E[num].v = b;
	head[a] = num;
}
void solve()
{
	minimum = INF;
	memset(cost,0x3f,sizeof(cost));
	for(int i = 1;i <= n;++i)
	{
		if(!dfn[i]) tarjan(i);
	}
	memset(head,0,sizeof(head));
	for(int i = 1;i <= m;++i)
	{
		int u = e[i].u,v = e[i].v;
		if(scc[u] != scc[v])
		{
			add(G,scc[u],scc[v],num_edge2);
			++ind[scc[v]];	
		}
	}
	int flag = YES;
	for(int i = 1;i <= num_scc;++i)
	{
		if(ind[i] == 0)
		{
			if(cost[i] < INF) ans += cost[i];
			else{
				flag = NO;
				minimum = min(minimum,Ins[i]);
			}
		}
	}
	if(flag == YES) printf("YES\n%d",ans);
	else printf("NO\n%d",minimum);
}
int main()
{
	scanf("%d%d",&n,&p);
	for(int i = 1;i <= p;++i)
	{
		int k;
		scanf("%d",&k);
		scanf("%d",&pr[k]);
	}
	scanf("%d",&m);
	for(int i = 1;i <= m;++i)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		add(e,a,b,num_edge1);
	}
	solve();
	return 0;
}
posted @ 2019-05-22 21:32  Zforw  阅读(11)  评论(0编辑  收藏  举报