*题解:P5008 [yLOI2018] 锦鲤抄

题目链接

解析

考虑 DAG 怎么做。我们知道在拓扑排序的过程中,每次会删掉一个没有入度的点。而题目要求删有入度的点,故将要删的点按照拓扑序的逆序来删,注意不能删源点。取除源点外点权最大的 \(k\) 个点来删即可。

DAG 可做,我们考虑缩点。考虑一个强连通分量可以怎么删,若缩完点后该强连通分量对应的点有入度,猜想这种情况其内部的点都可删,事实上从某个强连通分量外部连接到的内部的点开始遍历,按照 DFS 序的逆序来删即可。若没有入度,同理,有且仅有一个点不能删,这个点可以是任意点。需要注意的是强连通分量内部有自环也能全删。

故最终仍然是按照点权从大到小能取则取。时间复杂度 \(O(n\log n + m)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 5e5 + 10,M = 300000 + 5,K = 10000 + 5;
int dfn[N],low[N],cnt,pcnt[N],siz[N],num[N],ins[N],ind[N];
vector<int> scc[N],g[N],g2[N];
int scnt;
stack<int> s;
void dfs(int x){
	low[x] = dfn[x] = ++cnt;
	s.push(x);
	ins[x] = true;
	for(int nx : g[x]){
		if(!dfn[nx]){
			dfs(nx);
			low[x] = min(low[x],low[nx]);
		}else if(ins[nx]){
			low[x] = min(low[x],low[nx]);
		}
	}
	if(low[x] >= dfn[x]){
		int t = -1;
		scnt++;
		do{
			t = s.top();
			scc[scnt].push_back(t);
			s.pop();
			ins[t] = false;
			siz[scnt]++;
			num[t] = scnt;
		}while(t != x);
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);	
//	freopen("in.txt","r",stdin);
//	freopen("out1.txt","w",stdout);
	int n,m,k;
	cin>>n>>m>>k;
	vector<pii> v;
	for(int i=1;i<=n;i++){
		int w;
		cin>>w;
		v.push_back({w,i});
	}
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			dfs(i);
		}
	}
	for(int i=1;i<=n;i++){
		for(int j : g[i]){
			int u = num[i],v = num[j];
			if(u != v || i == j){
				g2[u].push_back(v);
				ind[v]++;
			}
		}
	}
	sort(v.begin(),v.end(),greater<pii>());
	int now = 0,res = 0;
	for(int i=0;i<v.size() && now < k;i++){
		int x = num[v[i].second];
		if(pcnt[x] == siz[x] - 1 && !ind[x]) continue;
		pcnt[x]++;
		res += v[i].first;
		now++;
	}
	cout<<res;
	return 0;
}
/*
*/
posted @ 2026-08-23 12:37  yutar  阅读(7)  评论(0)    收藏  举报