*题解: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;
}
/*
*/

浙公网安备 33010602011771号