简化版题意:
给你一张有向图,每个点有一个点权。任意时刻你可以任意选择一个有入度的点,获得它的点权并把它和它的出边从图上删去。最多能选择 kkk 个点,求最多能获得多少点权。
输入格式
输入的第一行是三个用空格隔开的整数,代表图的点数 nnn 和边数 mmm 以及点数的限制 kkk。
输入的第二行是 nnn 个用空格隔开的整数,第 iii 个数 wiw_iwi 代表点 iii 的火力值(点权)。
第 333 到第 (m+2)(m + 2)(m+2) 行,每行两个用空格隔开的整数 u,vu, vu,v,代表一条 uuu 指向 vvv 的有向边。
输出格式
输出一行一个整数,代表最大的火力值。
好题!!
先缩点,然后分类讨论
如果该强联通分量有入度,那可以全部都有机会被删
如果没有,则必需留下一个点,那就把最小的留下,其他全部加进序列里来
排序一下,取前k个
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=5e5+10,M=2e6+10;
int nxt[M],head[N],go[M],tot;
inline void add(int u,int v){
nxt[++tot]=head[u],head[u]=tot,go[tot]=v;
}
inline int read(){
int x=0,f=1; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n,m,k,a[N];
vector<int>be[N];
int dfn[N],low[N],co[N],st[N],top,num,col;
void Tarjan(int u){
dfn[u]=low[u]=++num;
st[++top]=u;
for(int i=head[u];i;i=nxt[i]){
int v=go[i];
if(!dfn[v]){
Tarjan(v);
low[u]=min(low[u],low[v]);
}else if(!co[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
co[u]=++col;
be[col].push_back(a[u]);
while(st[top]!=u){
co[st[top]]=col;
be[col].push_back(a[st[top]]);
--top;
}
--top;
}
}
vector<int>ans;
struct node{
int u,v;
}e[M];
inline bool cmp(int x,int y){
return x>y;
}
int vis[N];
signed main(){
n=read(),m=read(),k=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1,u,v;i<=m;i++){
u=read(),v=read();
e[i]=(node){u,v};
add(u,v);
}
for(int i=1;i<=n;i++)if(!dfn[i])Tarjan(i);
for(int i=1;i<=m;i++){
if(co[e[i].u]==co[e[i].v])continue;
vis[co[e[i].v]]=1;
}
for(int i=1;i<=col;i++){
if(vis[i])for(int j=0;j<be[i].size();j++)ans.push_back(be[i][j]);
else {
sort(be[i].begin(),be[i].end());
for(int j=1;j<be[i].size();j++)ans.push_back(be[i][j]);
}
}
sort(ans.begin(),ans.end(),cmp);
int res=0;
for(int i=0;i<k;i++)res+=ans[i];
cout<<res<<endl;
}