间谍网络
题目描述
由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。
我们的反间谍机关提供了一份资料,包括所有已知的受贿的间谍,以及他们愿意收受的具体数额。同时我们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有n个间谍(n不超过3000),每个间谍分别用1到3000的整数来标识。
请根据这份资料,判断我们是否有可能控制全部的间谍,如果可以,求出我们所需要支付的最少资金。否则,输出不能被控制的一个间谍。
输入格式
第一行只有一个整数n。
第二行是整数p。表示愿意被收买的人数,1≤p≤n。
接下来的p行,每行有两个整数,第一个数是一个愿意被收买的间谍的编号,第二个数表示他将会被收买的数额。这个数额不超过20000。
紧跟着一行只有一个整数r,1≤r≤8000。然后r行,每行两个正整数,表示数对(A, B),A间谍掌握B间谍的证据。
输出格式
如果可以控制所有间谍,第一行输出YES,并在第二行输出所需要支付的贿金最小值。否则输出NO,并在第二行输出不能控制的间谍中,编号最小的间谍编号。
输入输出样例
输入
3
2
1 10
2 100
2
1 3
2 3
输出
YES
110
输入
4
2
1 100
4 200
2
1 2
3 4
输出
NO
3
比较模板的一道题了,我们可以先特判输出NO的情况,也就是这个间谍不能被收买,并且其他间谍没有他的证据。
然后就是输出YES的情况了,我们就先缩点,然后入度为0的点中找最小的相加即可。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=30010;
int n,p,r,u[N],v[N],vis[N],dfn[N],low[N],cnt,co,col[N],in[N],flag,res;
int head[N],nex[N<<2],to[N<<2],tot,know[N];
stack<int> st;
map<int,int> mp;
inline void add(int a,int b){
to[++tot]=b; nex[tot]=head[a]; head[a]=tot;
}
void tarjan(int x){
dfn[x]=low[x]=++cnt; st.push(x); vis[x]=1;
for(int i=head[x];i;i=nex[i]){
if(!dfn[to[i]]){
tarjan(to[i]); low[x]=min(low[x],low[to[i]]);
}else if(vis[to[i]]) low[x]=min(low[x],dfn[to[i]]);
}
if(low[x]==dfn[x]){
co++;
while(1){
int u=st.top(); st.pop(); vis[u]=0;
col[u]=co; if(u==x) break;
}
}
}
signed main(){
cin>>n>>p;
for(int i=1;i<=p;i++){
int a,b; cin>>a>>b; mp[a]=b;
}
cin>>r;
for(int i=1;i<=r;i++){
cin>>u[i]>>v[i]; add(u[i],v[i]); know[v[i]]=1;
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++) if(!mp[i]&&!know[i]) return printf("NO\n%d\n",i),0;
for(int i=1;i<=r;i++) if(col[u[i]]!=col[v[i]]) in[col[v[i]]]++;
for(int i=1;i<=co;i++){
if(!in[i]){
int id=-1;
for(int j=1;j<=n;j++){
if(col[j]!=i) continue;
if(mp[j]&&(id==-1||mp[j]<mp[id])) id=j;
}
res+=mp[id];
}
}
printf("YES\n%d\n",res);
return 0;
}