LG P5008 [yLOI2018] 锦鲤抄 Solution

Description

给你一张 nnnmmm 边的有向GGG(可能有自环),每个点有一个点权 wiw_iwi,还有一个变量 SSS,初始时 S=0S=0S=0.
你可以任意选择一个入度 >0>0>0 的点 uuu,把 uuuuuu 的全部出边删掉,并让 S←S+wuS\gets S+w_uSS+wu.
你最多能执行 kkk 次这样的操作,求 SSS 的最大可能值.

Limitations

1≤n≤5×105+41\le n\le 5\times 10^5 +41n5×105+4
1≤m≤2×106+41\le m\le 2\times 10^6+41m2×106+4
0≤wi≤103,0≤k≤n0\le w_i\le 10^3,0\le k\le n0wi103,0kn

Solution

这题有紫吗?
考虑若 GGG 是个 DAG,那么只要按照 topo 序倒序删,所有入度 >0>0>0 的点都能删干净,选 wwwkkk 大的即可.
否则,可以把图缩成 DAG 变成上面情况,然后只需考虑 SCC 内部如何选.

  • 若一个 SCC 有入度,那么所有点都能删干净.
  • 若一个 SCC 没有入度,那么只有一个点无法删掉,我们显然要留下 www 最小的点.
  • 若一个 SCC 有自环,那么所有点也是能删干净的,需要特判.

找出所有候选点后,排序即可求出答案(当然用 nth_element 更快).

Code

1.92KB,1.19s,66.13MB  (c++20 with o2)1.92\text{KB},1.19\text{s},66.13\text{MB}\;\texttt{(c++20 with o2)}1.92KB,1.19s,66.13MB(c++20 with o2)

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

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, m, k;
	cin >> n >> m >> k;
	
	vector<int> w(n);
	for (int i = 0; i < n; i++) cin >> w[i];
	
	vector<vector<int>> g(n);
	for (int i = 0, u, v; i < m; i++) {
		cin >> u >> v, u--, v--;
		g[u].push_back(v);
	}
	
	vector<int> dfn(n, -1), low(n, -1), bel(n, -1), scc;
	vector<bool> ins(n);
	stack<int> stk;
	int clock = 0;
	
	auto dfs = [&](auto&& self, int u) -> void {
		dfn[u] = low[u] = clock++;
	    ins[u] = true, stk.push(u);
		for (auto v : g[u]) {
			if (dfn[v] == -1) self(self, v), chmin(low[u], low[v]);
			else if (ins[v]) chmin(low[u], dfn[v]);
		}
		
		if (low[u] == dfn[u]) {
			int id = bel[u] = scc.size();
			scc.push_back(u), ins[u] = false;
			
			int v = stk.top(); stk.pop();
			for (; v != u && !stk.empty(); v = stk.top(), stk.pop()) {
				bel[v] = id;
				if (w[v] < w[scc[id]]) scc[id] = v;
				ins[v] = false;
			}
		}
	};

	for (int i = 0; i < n; i++) if (dfn[i] == -1) dfs(dfs, i);
	
	vector<int> ind(scc.size());
	vector<bool> loop(scc.size());
	for (int u = 0; u < n; u++) 
		for (auto v : g[u]) {
			if (bel[u] != bel[v]) ind[bel[v]]++;
			else if (u == v) loop[bel[u]] = true;
		}
	
	vector<int> candi;
	for (int u = 0; u < n; u++) 
	    if (ind[bel[u]] || scc[bel[u]] != u || loop[bel[u]])
	        candi.push_back(w[u]);
	
	if (candi.size() < k) candi.resize(k);
	nth_element(candi.begin(), candi.begin() + k, candi.end(), greater<int>());
	
	int ans = 0;
	for (int i = 0; i < k; i++) ans += candi[i];
	cout << ans;
	
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值