http://acm.hdu.edu.cn/showproblem.php?pid=6611
题很简单,一眼拆点费用流
就是点边拉满之后复杂度有点恐怖,比赛的时候没敢莽费用流
但是最后居然真的是费用流,不过必须上原始对偶且用Dijkstra增广
具体细节很多,大概就是指,原本的Dijktra无法处理负权图,我们就去想办法对所有的费用进行统一扩大,变成正权最短路.
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#pragma GCC optimize(2)
const int maxn=4e3+30;
const int INF=1e9;
using namespace std;
struct mcf{public:
#define tpp int
struct node{short to;tpp cap;tpp cost;int rev;};
short prev[maxn],pree[maxn];
tpp dis[maxn],cost,h[maxn];
int f;
vector<node> g[maxn];
void init(int n=maxn-2){rep(i,0,n+1) g[i].clear();}
inline void add(int from,int to,tpp cap,tpp cost){
g[from].push_back({to,cap,cost,(int)g[to].size()});
g[to].push_back({from,0,-cost,(int)g[from].size()-1});
}
tpp getcost(int s,int t,int flow=INF){
f=0,cost=0;
fill(h,h+1+t,0);
while(flow){
#define pdi pair<tpp,short>
priority_queue<pdi,vector<pdi>,greater<pdi> >que;
fill(dis,dis+t+1,INF);
dis[s]=0;que.push(mp(0,s));
while(!que.empty()){
auto now=que.top();que.pop();
if(dis[now.se]<now.fi)continue;
int x=now.se,cnt=0;
for(auto &i:g[x])
if(i.cap>0&&dis[i.to]>dis[x]+h[x]-h[i.to]+i.cost){
dis[i.to]=dis[x]+i.cost+h[x]-h[i.to];
prev[i.to]=x,pree[i.to]=cnt++;
que.push(mp(dis[i.to],i.to));
}else cnt++;
}
if(dis[t]==INF)break;
rep(i,0,t+1) h[i]+=dis[i];
tpp d=flow;
for(int now=t;now!=s;now=prev[now])d=min(d,g[prev[now]][pree[now]].cap);
flow-=d,f+=d;cost+=d*h[t];
for(int now=t;now!=s;now=prev[now]){
node &e=g[prev[now]][pree[now]];
e.cap-=d,g[now][e.rev].cap+=d;
}
}
return cost;
}
}net;
int casn,n,k,m;
int a[maxn/2];
int main() {IO;
cin>>casn;
while(casn--){
cin>>n>>k;
net.init(n*2+10);
int ss=n*2+1,t=n*2+3,s=n*2+2;
rep(i,1,n) cin>>a[i];
rep(i,1,n){
net.add(i,i+n,1,-a[i]);
net.add(ss,i,1,0);
net.add(i+n,t,1,0);
rep(j,i+1,n) if(a[i]<=a[j]) net.add(i+n,j,1,0);
}
net.add(s,ss,k,0);
cout<<-net.getcost(s,t)<<endl;
}
}