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
%d",ans);
else printf("NO
%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;
}