【题解】[NOI2019] 序列(反悔贪心/模拟费用流)

反悔贪心: 能够获得局部最优解 加入和反悔的机制 以得到全局最优解

网络流中的退流操作本质上也是反悔贪心

回顾费用流的过程(基于EK):

在残余网络上跑出一条基于 cost 的最短路(s -> t),然后顺次更新

一般的实现时在贪心之后加入一个和贪心值相反的值或重构问题使得变成原问题等方式实现。

以下流的表达方式参照blogs

NOI2019 序列

建图略

自由度为 \(k - l\)

分析:

匹配的 生成/改变 是怎么在图上的退流体现的
即从推流出发观察 选择/反悔

  • \(i \to i'\) //价值:未选的最大的 $a_i + b_i $

  • \(j \to u \to v \to k'\)
    new: \(i \to u \gets j \to j'\)
    变为 \(j \to j'\)\(i \to k'\) // 价值:\(i\)(未选的 \(a\) 中的最大) + \(j'\)(已选的 \(a\) 中最大未选的 \(b\))

  • \(k\to u\to v\to i'\) new: \(i\to i'\gets v\to j'\)
    变为 \(i\to i' 和 k\to j'\) // 价值:\(i\)(已选的 \(b\) 中最大未选的 \(a\)) + \(j'\)(未选的 \(b\) 中的最大)

  • \(i\to u\to v\to j'\)
    // 价值;\(i\)(未选的 \(a\) 中的最大) + \(j'\)(未选的 \(b\) 中的最大)

  • \(i\to u\to v\to j'\)
    new:\(j\to j'\to v\to u\to i\to i'\)
    变为 \(i\to i'\)\(j\to j'\)

对于最后一个:

在图上,如何匹配是人为加的,即如果 \(u\),\(v\) 之间有流量,可认为任意满足条的 \(i\)\(j'\) 相互匹配(条件:选了 \(a\) 没选 \(b\) 或选了 \(b\) 没选 \(a\)

价值:\(i'\)(已选的 \(a\) 中最大未选的 \(b\)) + \(j\)(已选的 \(b\) 中最大未选的 \(a\))

最终用大根堆维护:

  • 未选的 \(a\) 中的最大
  • 未选的 \(b\) 中的最大
  • 已选的 \(a\) 中最大未选的 \(b\)
  • 已选的 \(b\) 中最大未选的 \(a\)
  • 未选的最大的 \(a_i + b_i\)

贪心的时候要维护自由度,看看有些关于 \((u, v)\) 的能不能产生退流

还要注意的是,值相同时要把 (未选的最大的 \(a_i + b_i\)) 这一决策优先,因为这样使得自由度增加,后面有机会更优

#include<bits/stdc++.h>
using namespace std;

namespace IO{
template <typename T>
inline void read(T &x){
	x = 0;
	static char c;
	static bool f;
	c = getchar(), f = 0;
	while(c<'0'||c>'9'){ if(c == '-')f = 1; c = getchar(); }
	while('0'<=c&&c<='9')x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
	x = f ? -x : x;
}
template <typename T,typename ...Types>
inline void read(T &x, Types &...y){
	read(x), read(y...);
}
}using namespace IO;


using ll = long long;
const int N = 200010;
const int inf = 1e9;
using pii = pair<int,int>;
#define fi first
#define se second
#define rep(i, l, r) for(int (i) = (l);(i)<=(r);++(i))
#define per(i, r, l) for(int (i) = (r);(i)>=(l);--(i))
#define id top().se

int T;
int n, K, L;
int a[N], b[N];
bool visa[N],visb[N];
priority_queue<pii> q,qa,qb,qa1,qb1;
// qa1 选了的 b 中没选的 a
// qb1 选了的 a 中没选的 b 


inline void insa(int x){
	visa[x] = 1;
	if(!visb[x])qb1.emplace(b[x], x);
}
inline void insb(int x){
	visb[x] = 1;
	if(!visa[x])qa1.emplace(a[x], x);
}

void solve(){
	read(n, K, L); L = K - L;
	rep(i, 1, n)read(a[i]);
	rep(i, 1, n)read(b[i]);
	rep(i, 1, n){
		visa[i] = visb[i] = 0;
		q.emplace(a[i] + b[i], i);
		qa.emplace(a[i], i);
		qb.emplace(b[i], i);
	}
	ll ans = 0;
	q.emplace(-inf, 0);
	qa.emplace(-inf, 0), qb.emplace(-inf, 0);
	qa1.emplace(-inf, 0), qb1.emplace(-inf, 0);
	rep(i, 1, K){//共 k 次匹配,k 次增广 
		while(visa[q.id] || visb[q.id])q.pop();
		while(visa[qa.id])qa.pop();
		while(visb[qb.id])qb.pop();
		while(visa[qa1.id])qa1.pop();
		while(visb[qb1.id])qb1.pop();
		pii now = q.top(), nowa = qa.top(), nowb = qb.top();
		pii nowa1 = qa1.top(), nowb1 = qb1.top();
		int mx = now.fi, op = 1;
		if(mx < nowa1.fi + nowb1.fi)mx = nowa1.fi + nowb1.fi, op = 5;
		if(mx < nowa.fi + nowb1.fi)mx = nowa.fi + nowb1.fi, op = 2;
		if(mx < nowb.fi + nowa1.fi)mx = nowb.fi + nowa1.fi, op = 3;
		if(L && nowa.se != nowb.se)if(mx < nowa.fi + nowb.fi)
			mx = nowa.fi + nowb.fi, op = 4;
		ans += mx;
		if(op == 1)insa(now.se), insb(now.se);
		if(op == 2)insa(nowa.se), insb(nowb1.se);
		if(op == 3)insa(nowa1.se), insb(nowb.se);
		if(op == 4)insa(nowa.se), insb(nowb.se), --L;
		if(op == 5)insa(nowa1.se), insb(nowb1.se), ++L;
	}
	printf("%lld\n",ans);
	while(q.size())q.pop();
	while(qa.size())qa.pop();
	while(qb.size())qb.pop();
	while(qa1.size())qa1.pop();
	while(qb1.size())qb1.pop();
}

int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	read(T);
	while(T--)solve();
    return 0;
}
posted @ 2025-05-13 21:32  Luzexxi  阅读(41)  评论(0)    收藏  举报