[贪心] [有向图连通性] [图论] P5008 [yLOI2018] 锦鲤抄

posted on 2024-06-20 13:21:20 | under | source

题意:\(n\)\(m\) 边的有向图,点带权,每次可以删去一个入度非 \(0\) 的点,至多删 \(k\) 个,问删去的点的权值和最大多少?\(k\le n\le 5\times 10^5,m\le 2\times 10^6\)

考虑一个 \(\rm DAG\)。容易发现,可以不断删去出度为 \(0\) 的点(即倒着跑拓扑),这样的话不会影响其它点的入度。到最后就能取到所有度数非 \(0\) 的点了。这是上界,所以一定最优。

但是上述做法不适用于环,因为这样就会把所有点删掉。事实上,可以“保留”一个点,提供入度,然后对于其它的点,可以类似 \(\rm DAG\) 的方法全删掉。

于是对它进行缩点,得到由若干 \(\rm SCC\) 构成的一张 \(\rm DAG\),那么对大点倒着跑拓扑,入度不为 \(0\)\(\rm SCC\) 中的所有点均可以删掉,只要令被其它 \(\rm SCC\) 指向的那个点最后一个被删就好了。入度为 \(0\) 就按对环的讨论“保留”一个点。

于是兴致勃勃地码完代码,结果直接 WA 了——没有考虑到自环:对于一个含自环的 \(\rm SCC\),一定能全删掉,令这个自环最后删即可。

然后将点按权从大到小遍历,如果满足上述判定条件就取,这样取至多 \(k\) 个即可。

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N = 5e5 + 5;
int n, m, k, a[N], u, v, _ru[N], ans;
int dfn[N], low[N], vis[N], stk[N], st, df, scc, col[N], ru[N], siz[N], _circle[N], circle[N];
vector<int> to[N];
priority_queue<pair<int, int> > q;

inline void dfs(int u){
	dfn[u] = low[u] = ++df, vis[u] = 1, stk[++st] = u;
	for(auto v : to[u]){
		if(vis[v] == 2) continue;
		if(!vis[v]) dfs(v);
		low[u] = min(low[u], low[v]);
	}
	if(dfn[u] == low[u]){
		int tp; ++scc;
		do{
			tp = stk[st--];
			col[tp] = scc, ++siz[scc], circle[scc] |= _circle[tp];
		}while(tp ^ u);
	}
}
signed main(){
	cin >> n >> m >> k;
	for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
	for(int i = 1; i <= m; ++i) scanf("%lld%lld", &u, &v), to[u].push_back(v), ++_ru[v], _circle[u] |= (u == v);
	for(int i = 1; i <= n; ++i) if(!vis[i]) dfs(i);
	for(int i = 1; i <= n; ++i){
		if(_ru[i]) q.push({a[i], i});
		for(auto v : to[i]) if(col[i] ^ col[v]) ++ru[col[v]];
	}
	while(k && !q.empty()){
		int val = q.top().first, u = q.top().second;
		q.pop();
		if(!circle[col[u]] && !ru[col[u]] && siz[col[u]] == 1) continue;
		--siz[col[u]], ans += val, --k;
	}
	cout << ans;
	return 0;
}
posted @ 2026-01-13 11:20  Zwi  阅读(0)  评论(0)    收藏  举报