[NOI2019]序列 [贪心,模拟费用流]

传送门

首先你需要知道网络流的建图方法,偷一张图

[NOI2019]序列 [贪心,模拟费用流]_第1张图片

我开始没有想到怎么限制 L 个相同,我们何不先选出 k 对,然后把 k 对拆开调整两边的最大选择

新建两个点 C,D 来给两边调整的空间

我们可以模拟这个网络流的运作过程

类似某些贪心的题,用堆来维护最大之类的

我们可以记录 CD 可以通过的流量 flow,如果 CD 有流量,我们就可以在两边分别选最大

如果没有,我们可以选一个A,保证B在对面出现,然后在对面随便选一个B

或者选一个B, 保证A在对面出现,然后随便选一个 A

或者选一对最大的 A,B

我们发现 堆需要维护以下东西

没有出现的 A, 没有出现的 B,出现了B没有出现的A, 出现了A没有出现的B,AB都没有出现的

然后就可以模拟了!


#include
#define N 200050
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
typedef long long ll;
int T, n, k, L, a[N], b[N];
int sta[N], id[N];
ll ans, Flow;
struct A{ int id; bool operator < (const A &x) const{ return a[id] < a[x.id];} }tmp1;
struct B{ int id; bool operator < (const B &x) const{ return b[id] < b[x.id];} }tmp2;
struct AB{ int id; bool operator < (const AB &x) const{ return a[id] + b[id] < a[x.id] + b[x.id];} }tmp3;
priority_queue q1, p1;
priority_queue q2, p2;
priority_queue q;

bool cmpa(int x, int y){ return a[x] > a[y];}
bool cmpb(int x, int y){ return b[x] > b[y];}

void Init(){
	while(!q1.empty()) q1.pop();
	while(!p1.empty()) p1.pop();
	while(!q2.empty()) q2.pop();
	while(!p2.empty()) p2.pop();
	while(!q.empty()) q.pop();
	ans = Flow = 0;
	memset(sta, 0, sizeof(sta));
}
void Prework(int x){
	for(int i=1; i<=n; i++) id[i] = i;
	sort(id+1, id+n+1, cmpa);
	for(int i=1; i<=x; i++) ans += a[id[i]], sta[id[i]] |= 1;
	sort(id+1, id+n+1, cmpb);
	for(int i=1; i<=x; i++) ans += b[id[i]], sta[id[i]] |= 2; 
}
void Pop(){
	while(!q1.empty() && sta[q1.top().id] & 1) q1.pop();
	while(!p1.empty() && sta[p1.top().id] ^ 2) p1.pop();
	while(!q2.empty() && sta[q2.top().id] & 2) q2.pop();
	while(!p2.empty() && sta[p2.top().id] ^ 1) p2.pop();
	while(!q.empty() && sta[q.top().id]) q.pop(); 
}
void Update1(){ // 任意选 
	int x = q1.top().id, y = q2.top().id;
	Flow--;
	ans += a[x] + b[y];
	sta[x] |= 1; sta[y] |= 2;
	if(sta[x] == 1) tmp2.id = x, p2.push(tmp2);
	if(sta[y] == 2) tmp1.id = y, p1.push(tmp1);
	Flow += (x == y) ? 1 : ((sta[x] == 3) + (sta[y] == 3));
}
void Update2(){
	int s1 = 0, s2 = 0, s3 = 0;
	if(!p1.empty()) s1 = a[p1.top().id] + b[q2.top().id];
	if(!p2.empty()) s2 = a[q1.top().id] + b[p2.top().id];
	if(!q.empty()) s3 = a[q.top().id] + b[q.top().id];
	int mx = max(max(s1, s2), s3); ans += mx; 
	if(s1 == mx){
		int x = p1.top().id, y = q2.top().id;
		sta[x] |= 1; sta[y] |= 2;
		if(sta[y] == 3) Flow++;
		else tmp1.id = y, p1.push(tmp1); 
		return;
	}
	if(s2 == mx){
		int x = q1.top().id, y = p2.top().id;
		sta[x] |= 1; sta[y] |= 2;
		if(sta[x] == 3) Flow++;
		else  tmp2.id = x, p2.push(tmp2);
		return;
	}
	if(s3 == mx){ int x = q.top().id; sta[x] = 3;}
}
int main(){
	T = read();
	while(T--){
		n = read(), k = read(), L = read();
		for(int i=1; i<=n; i++) a[i] = read();
		for(int i=1; i<=n; i++) b[i] = read();
		Init(); Prework(k - L);
		for(int i=1; i<=n; i++){
			tmp1.id = i; tmp2.id = i; tmp3.id = i;
			if(sta[i] == 0){ q1.push(tmp1); q2.push(tmp2); q.push(tmp3);}
			if(sta[i] == 1){ p2.push(tmp2); q2.push(tmp2);}
			if(sta[i] == 2){ p1.push(tmp1); q1.push(tmp1);}
			if(sta[i] == 3) Flow++;
		}
		while(L--){
			Pop();
			if(Flow) Update1();
			else Update2();
		} printf("%lld\n", ans);
	} return 0;
}

 

你可能感兴趣的:(贪心,网络流,FSY的好题汇总)