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