原题链接https://www.luogu.com.cn/problem/P1262https://www.luogu.com.cn/problem/P1262
Tarjian原理https://www.cnblogs.com/shadowland/p/5872257.html
ACCODE
#include <bits/stdc++.h>
using namespace std;
#define maxn 10010
#define inf 0x3f3f3f3f
/*
@time表示tarjian的时间戳,即深搜的次序编号
@cnt表示强连通分量的个数(维护)
@buy[i]表示可以被收买的间谍,i表示间谍的编号,buy[i]表示收买金额
@dn[i]表示tarjian算法编号为i的间谍的时间戳,即第几个被访问
@low[i]表示tarjian算法中编号为i的间谍,能够去到的所有点中时间戳最小的
# explanation: scc - strongly connected component 强连通分量
@scc_min_id[i]表示第i个强连通分量中最小的编号
@scc_min_cost[i]表示第i个强连通分量中,收买金额最小的间谍
@k[i]表示i号间谍在哪个scc中
@scc_ru[i]表示第i个强连通分量(缩点后)有没有入度
@vis[i]表示i号间谍有没有在栈里
@connect_scc[i][j]表示i号scc和j号scc有没有连边
*/
int n, p, r, timee, cnt;
int buy[maxn], dn[maxn], low[maxn];
int scc_min_id[maxn], scc_min_cost[maxn], k[maxn];
bool vis[maxn], connect_scc[maxn][maxn], scc_ru[maxn];
vector<int> edge[maxn];
stack<int> s;
void tarjian(int x)
{
low[x] = dn[x] = ++timee;
s.push(x);
vis[x] = true;
for (int i = 0; i < edge[x].size(); i++)
{
int y = edge[x][i];
if (!dn[y])
{
tarjian(y);
low[x] = min(low[x], low[y]);
}
else if (vis[y])
{
low[x] = min(low[x], low[y]);
}
}
if (dn[x] == low[x])
{
int min_cost = inf, id = inf, t;
cnt++;
while (1)
{
t = s.top();
s.pop();
vis[t] = false;
min_cost = min(min_cost, buy[t]);
id = min(id, t);
k[t] = cnt;
if (t == x)
break;
}
scc_min_cost[cnt] = min_cost;
scc_min_id[cnt] = id;
}
}
int main()
{
cin >> n >> p;
for (int i = 1; i <= n; i++)
buy[i] = inf;
for (int k = 1; k <= p; k++)
{
int i, j;
cin >> i >> j;
buy[i] = j;
}
cin >> r;
for (int k = 1; k <= r; k++)
{
int i, j;
cin >> i >> j;
edge[i].push_back(j);
}
for (int i = 1; i <= n; i++)
if (!dn[i])
tarjian(i);
for (int i = 1; i <= n; i++)
for (int j = 0; j < edge[i].size(); j++)
{
int m = edge[i][j];
//如果这条边上两个点都在一个scc上,或者在不同的scc上但是已经判断连通了就continue
if (k[i] == k[m] || connect_scc[k[i]][k[m]])
continue;
connect_scc[k[i]][k[m]] = true;
//如果i编号所在的scc没有入度,且没有人能收买,那么这个scc相当于孤立了,那么与它连通的scc也相当于没有入度
if (scc_ru[k[i]] || scc_min_cost[k[i]] != inf)
scc_ru[k[m]] = true;
}
int id = inf, ans = 0;
bool tag = false;
//遍历scc
for (int i = 1; i <= cnt; i++)
{
//有入度的scc表示符合
if (scc_ru[i])
continue;
if (scc_min_cost[i] == inf) //这个强联通分量不能买 说明整张图就无解了
{
tag = true;
id = min(id, scc_min_id[i]); // 找到最小编号
}
if (!tag)
ans += scc_min_cost[i];
}
if (tag)
cout << "NO" << endl
<< id;
else
cout << "YES" << endl
<< ans;
return 0;
}