[SCC]间谍网络

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

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

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

输入 第一行只有一个整数 nn。第二行是整数pp。表示愿意被收买的人数。

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

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

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

样例输入
2
1
2 512
2
1 2
2 1
样例输出
YES
512

提示 1≤n≤3000,1≤p≤n,1≤r≤80001≤n≤3000,1≤p≤n,1≤r≤8000,
每个收买的费用为非负数且不超过2000020000。

分析:
SCC,这类关系传递的题先缩点,缩点时记录每个新点中的最小花费和最小编号,那么我们能控制整个网络,当且仅当可以贿赂所有入度为0的点,因为没有任何人有他们的情报

最小花费即所有入度为0的点的最小花费和,而最小编号不一定在入度为0的点中,比如2->1->3 仅3接受贿赂,所以此时我们要统计入度为0的点及其后继点中入度为1的点的最小编号

此时还有一个问题:2->3 3->1 4->1 此时1号点的入度为2,但也要统计,如何实现?很简单,从一个入度为0的点搜索到第一个入读不为1的点时把这个点的入度-1就行了

代码:(仍然是CSDN的锅,请自行复制)

#include<bits/stdc++.h>
const int INF=0x3f;
using namespace std;
const int N=100005,M=200005;
int n,m;
int a[N],in[N]={0},nExt[N]={0};
int ans=1000000000,flag=1,total=0;
int fro[N],sta[N],insta[N]={0},top=0,tot=0,vis[M],head[M],nxt[M],id[N],sign=0,scc=0,low[N],dfn[N],mon[N],minn[N];
inline void add(int x,int y){fro[++tot]=x;vis[tot]=y,nxt[tot]=head[x];head[x]=tot;}
void tarjan(int v){
	low[v]=dfn[v]=++sign;
	sta[++top]=v;insta[v]=1;
	for(int i=head[v];i;i=nxt[i]){
		int y=vis[i];
		if(!dfn[y]) {tarjan(y);low[v]=min(low[y],low[v]);}
		else if(insta[y]) low[v]=min(low[v],dfn[y]);
	}
	if(low[v]==dfn[v]){
		++scc;int i=sta[top];
		while(i!=v) {mon[scc]=min(mon[scc],a[i]);minn[scc]=min(minn[scc],i);insta[i]=0;id[i]=scc;i=sta[--top];}
		insta[v]=0;id[v]=scc;top--;mon[scc]=min(mon[scc],a[v]);minn[scc]=min(minn[scc],v);
	}
}
void get_ans(int v){
	if(mon[v]==1000000000){
		ans=min(ans,minn[v]);v=nExt[v];
		flag=0;
		while(in[v]==1){
			ans=min(ans,minn[v]);
			v=nExt[v];	
		}
		in[v]--;
	}
	else total+=mon[v];
}
void sd(){
	for(int i=1;i<=m;i++)
	{
		if(id[fro[i]]!=id[vis[i]]){
			nExt[id[fro[i]]]=id[vis[i]];
			in[id[vis[i]]]++;
		}
	}
	for(int i=1;i<=scc;i++)
		if(in[i]==0) get_ans(i);
}
int main(){
	cin>>n;
	int p;
	cin>>p;
	for(int i=1;i<=n;i++) a[i]=1000000000,mon[i]=1000000000,minn[i]=1000000000;
	while(p--){
		int x,monn;
		scanf("%d%d",&x,&monn);
		a[x]=monn;
	}
	cin>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
	sd();
	if(!flag) cout<<"NO"<<endl<<ans;
	else cout<<"YES"<<endl<<total;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值