题目传送门
题面
题目描述
由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果 A 间谍手中掌握着关于 B 间谍的犯罪证据,则称 A 可以揭发 B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全部情报。所以,如果我们能够收买一些间谍的话,我们就可能控制间谍网中的每一分子。因为一旦我们逮捕了一个间谍,他手中掌握的情报都将归我们所有,这样就有可能逮捕新的间谍,掌握新的情报。
我们的反间谍机关提供了一份资料,包括所有已知的受贿的间谍,以及他们愿意收受的具体数额。同时我们还知道哪些间谍手中具体掌握了哪些间谍的资料。假设总共有 n n n 个间谍( n n n 不超过 3000 3000 3000),每个间谍分别用 1 1 1 到 3000 3000 3000 的整数来标识。
请根据这份资料,判断我们是否有可能控制全部的间谍,如果可以,求出我们所需要支付的最少资金。否则,输出不能被控制的一个间谍。
输入格式
第一行只有一个整数 n n n。
第二行是整数 p p p。表示愿意被收买的人数, 1 ≤ p ≤ n 1\le p\le n 1≤p≤n。
接下来的 p p p 行,每行有两个整数,第一个数是一个愿意被收买的间谍的编号,第二个数表示他将会被收买的数额。这个数额不超过 20000 20000 20000。
紧跟着一行只有一个整数 r r r, 1 ≤ r ≤ 8000 1\le r\le8000 1≤r≤8000。然后 r r r 行,每行两个正整数,表示数对 ( A , B ) (A, B) (A,B), A A A 间谍掌握 B B B 间谍的证据。
输出格式
如果可以控制所有间谍,第一行输出 YES,并在第二行输出所需要支付的贿金最小值。否则输出 NO,并在第二行输出不能控制的间谍中,编号最小的间谍编号。
输入输出样例 #1
输入 #1
3
2
1 10
2 100
2
1 3
2 3
输出 #1
YES
110
输入输出样例 #2
输入 #2
4
2
1 100
4 200
2
1 2
3 4
输出 #2
NO
3
思路
先遍历所有间谍。如果一个间谍可以被购买且未被访问过就用它做一次 Tarjan,控制每个强连通分量的成本为这个强连通分量价格最低的间谍。之后没访问过的间谍控制不了。如果可以控制所有间谍,容易知道应当购买入度为 0 0 0 的间谍。输出购买这些间谍的总花费即可。
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
int n,r,p;
int buy[3003];
vector <int> pict[3003];
int dfn[3003],low[3003],nowdfn = 1,buyscc[3003];
bool instack[3003];
int scc[3003],scs,ans;
int insum[3003];
stack <int> nowblock;
void tarjan(int now)
{
dfn[now] = low[now] = nowdfn++;
nowblock.push(now);
instack[now] = 1;
for(auto i : pict[now])
{
if(!dfn[i])
{
tarjan(i);
low[now] = min(low[now],low[i]);
}
else if(instack[i]) low[now] = min(low[now],dfn[i]);
}
if(dfn[now] == low[now])
{
scs++;
buyscc[scs] = -1;
while(nowblock.top() != now)
{
scc[nowblock.top()] = scs;
if(buy[nowblock.top()] != -1)
{
if(buyscc[scs] == -1) buyscc[scs] = buy[nowblock.top()];
else buyscc[scs] = min(buyscc[scs],buy[nowblock.top()]);
}
instack[nowblock.top()] = 0;
nowblock.pop();
}
if(buy[nowblock.top()] != -1)
{
if(buyscc[scs] == -1) buyscc[scs] = buy[nowblock.top()];
else buyscc[scs] = min(buyscc[scs],buy[nowblock.top()]);
}
scc[nowblock.top()] = scs;
instack[nowblock.top()] = 0;
nowblock.pop();
}
}
int main()
{
cin >> n >> p;
for(int i = 1;i <= n;i++) buy[i] = -1;
for(int i = 1;i <= p;i++)
{
int a,b;
cin >> a >> b;
buy[a] = b;
}
cin >> r;
for(int i = 1;i <= r;i++)
{
int a,b;
cin >> a >> b;
pict[a].push_back(b);
}
for(int i = 1;i <= n;i++) if(!dfn[i] && buy[i] != -1) tarjan(i);
for(int i = 1;i <= n;i++)
{
if(!dfn[i])
{
cout << "NO\n" << i;
return 0;
}
}
cout << "YES\n";
for(int i = 1;i <= n;i++)
{
for(auto j : pict[i])
{
if(scc[i] != scc[j]) insum[scc[j]]++;
}
}
for(int i = 1;i <= scs;i++)
{
if(!insum[i]) ans += buyscc[i];
}
cout << ans;
}
647

被折叠的 条评论
为什么被折叠?



