BZOJ 3593 Control(匹配,最優子結構,平衡樹優化DP)

題目
BZOJ真的炸了

首先藥水很容易解決,求出最優方案後,分兩種情況,一種是把最優方案中bb最大的變成00,一種是把不在最優方案中aa最小的變成00(因爲不在最優方案中的只有aa有用,bb都可以看做00),然後再求一遍最優方案。

這個最優方案我們用二分圖最小權匹配的視角來看待,那麼費用流建圖之後可以發現:

KMKM算法找最小權匹配的增廣過程與最小費用流的增廣過程我們可以知道,
在已經匹配中的點是不會因爲增廣而不在匹配中的。
也就是說,S和T周圍的邊一旦被增廣,就不會被退流。
這說明,這個問題具有最優子結構黑人問號
還是從匹配的角度來理解,那麼就是選kk個病人救的最小花費是在k1k-1個病人的最優方案上,再選一個不在最優方案中的最優選擇。
但是這個選不是指最後一個救,而是根據顯然的性質按aa從小到大把他插入到他應該的位置救。
那麼按aa從小到大排序後就可以在序列中分塊維護凸包再單調棧一下斜率即可,有刪除時重構。(似曾相識的毒瘤。
但是可以用平衡樹。
fi,jf_{i,j}表示前ii箇中選jj個的最小值。(這個前ii個你可以自己定義順序,沒什麼區別
那麼因爲最優子結構,所以我們每次加入一個點的時候是一個後綴的jj被修改,平衡樹上二分即可。
代碼實現十分巧妙,敬請開抄

#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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章