题目
BZOJ真的炸了
首先药水很容易解决,求出最优方案后,分两种情况,一种是把最优方案中最大的变成,一种是把不在最优方案中最小的变成(因为不在最优方案中的只有有用,都可以看做),然后再求一遍最优方案。
这个最优方案我们用二分图最小权匹配的视角来看待,那么费用流建图之后可以发现:
由算法找最小权匹配的增广过程与最小费用流的增广过程我们可以知道,
在已经匹配中的点是不会因为增广而不在匹配中的。
也就是说,S和T周围的边一旦被增广,就不会被退流。
这说明,这个问题具有最优子结构黑人问号
还是从匹配的角度来理解,那么就是选个病人救的最小花费是在个病人的最优方案上,再选一个不在最优方案中的最优选择。
但是这个选不是指最后一个救,而是根据显然的性质按从小到大把他插入到他应该的位置救。
那么按从小到大排序后就可以在序列中分块维护凸包再单调栈一下斜率即可,有删除时重构。(似曾相识的毒瘤。)
但是可以用平衡树。
表示前个中选个的最小值。(这个前个你可以自己定义顺序,没什么区别)
那么因为最优子结构,所以我们每次加入一个点的时候是一个后缀的被修改,平衡树上二分即可。
代码实现十分巧妙,敬请开抄
#include<bits/stdc++.h>
#define maxn 100005
#define LL long long
using namespace std;
int n,P,K,rt,fg;
int lc[maxn],rc[maxn],sz[maxn],key[maxn],c[maxn];
LL val[maxn],a[maxn],b[maxn],ad[maxn];
bool cmp(const int &u,const int &v){ return a[u] == a[v] ? b[u] > b[v] : a[u] > a[v]; }
void upd(int u){ sz[u] = sz[lc[u]] + sz[rc[u]] + 1; }
void dtp(int u,LL v){ val[u]+=v,ad[u]+=v; }
void dt(int u){ if(ad[u]) dtp(lc[u],ad[u]),dtp(rc[u],ad[u]),ad[u]=0; }
void split(int u,int &l,int &r,int sm,LL a,LL b){
if(!u) return (void)(l=r=0);
dt(u);
if(val[u] < a * (sm + sz[lc[u]]) + b) l = u , split(rc[u] , rc[l] , r , sm + sz[lc[u]] + 1 , a , b);
else r = u , split(lc[u] , l , lc[r] , sm , a , b);
upd(u);
}
void merge(int &u,int l,int r){
if(!l || !r) return (void)(u=l+r);
dt(l),dt(r);
if(key[l] < key[r]) u = l , merge(rc[u] , rc[l] , r);
else u = r , merge(lc[u], l , lc[r]);
upd(u);
}
void Sol(){
sort(c+1,c+1+n,cmp);
rt = 0;
for(int i=1,x,y;i<=n;i++){
int u=c[i];
split(rt,x,y,0,a[u],b[u]);
dtp(y,a[u]);
key[i] = rand() << 15 | rand();
lc[i] = rc[i] = ad[i] = 0 ,
val[i] = sz[x] * a[u] + b[u] , sz[i] = 1;
merge(x,x,i),merge(rt,x,y);
}
}
LL sm = 0 , ans = 0 , pt[maxn];
void Sol2();
void dfs(int u){
if(!u) return;
dt(u);
dfs(lc[u]);
sm += val[u] , pt[++pt[0]] = u;
if(pt[0] == P){
if(! K) printf("%lld\n",sm);
else if(!fg) fg=1,Sol2();
else printf("%lld\n",min(sm,ans));
exit(0);
}
dfs(rc[u]);
}
void Sol2(){
ans = sm; LL mx = 0;
static bool vis[maxn] = {};
for(int i=1;i<=P;i++) mx = max(mx , b[c[pt[i]]]) , vis[pt[i]] = 1;
ans -= mx;
for(int i=n;i>=1;i--) if(!vis[i]){
b[c[i]] = 0;
Sol();pt[0] = sm = 0;
dfs(rt);
break;
}
printf("%lld\n",ans);
exit(0);
}
int main(){
scanf("%d%d%d",&n,&P,&K);
for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]),c[i]=i;
Sol(),dfs(rt);
}