P5008 [yLOI2018] 锦鲤抄(Tarjan+贪心)
题意:
给出一个有向图,每次可以删除存在入度的点及其出边,每次删除一个点可以获得其权值。
问最终能够获得的最大权值为多少。
思路:
考虑DAG:我们直接倒着拓扑序来选,即可将所有入度不为\(0\)的点选完。
若不为DAG,考虑\(tarjan\)求出强连通分量,分析可以发现:对于一个单独的强连通分量,假设其点数为\(n\),那么可以选择\(n-1\)个点;若其入度不为\(0\),那么强连通分量中所有点都可以选择。
然后直接这样来搞就行。
证明...我也不会,在纸上画画就行了。
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
// #define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 5e5 + 5, M = 2e6 + 5;
struct Edge{
int v, next;
}e[M];
int a[N];
int head[N], tot;
void adde(int u, int v) {
e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
}
int n, m, k;
pii E[M];
stack <int> s;
int T, num;
int col[N], dfn[N], low[N], Min[N];
bool chk[N];
void Tarjan(int u){
dfn[u] = low[u] = ++T;
s.push(u);
for(int i = head[u]; i != -1;i = e[i].next){
int v = e[i].v;
if(!dfn[v]){
Tarjan(v);
low[u] = min(low[u], low[v]);
}else if(!col[v]){
low[u] = min(low[u], dfn[v]);
}
}
if(low[u] == dfn[u]){
num++; int now;
do{
now = s.top(); s.pop();
col[now] = num;
Min[num] = min(Min[num], a[now]);
}while(!s.empty() && now!=u);
}
}
int in[N];
void run() {
cin >> n >> m >> k;
memset(head, -1, sizeof(head)); tot = 0;
memset(Min, INF, sizeof(Min));
ll ans = 0;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
E[i] = MP(u, v);
adde(u, v);
}
for(int i = 1; i <= n; i++) {
if(!dfn[i]) Tarjan(i);
}
for(int i = 1; i <= m; i++) {
int u = E[i].fi, v = E[i].se;
if(col[u] != col[v]) ++in[col[v]];
}
for(int i = 1; i <= n; i++) {
int bel = col[i];
if(a[i] == Min[bel] && in[bel] == 0) {
if(!chk[bel]) {
chk[bel] = 1;
a[i] = 0;
}
}
}
sort(a + 1, a + n + 1); reverse(a + 1, a + n + 1);
for(int i = 1; i <= k; i++) ans += a[i];
pt(ans);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
run();
return 0;
}
重要的是自信,一旦有了自信,人就会赢得一切。