題目
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);
}