第一次凭本事做出 NOI 的 T3 。
首先有一个非常显然的费用流建模如下,边用二元组 ( c a p , c o s t ) (cap,cost) (cap,cost) 表示:
S S S 向 S ′ S' S′ 连 ( K , 0 ) (K,0) (K,0) 。 S ‘ ’ S‘’ S‘’ 分别向 i i i 点连 ( 1 , a i ) (1,a_i) (1,ai) , i i i 点向 i ′ i' i′ 点连 ( 1 , 0 ) (1,0) (1,0), i ′ i' i′ 点向 T T T 连 ( 1 , b i ) (1,b_i) (1,bi)。
这样跑出来全部都是选 a i a_i ai 就必须选 b i b_i bi 的情况,转化题意发现,我们允许最多 K − L K-L K−L 组 a i a_i ai 和 b j b_j bj 不匹配。
所以建立中间点 P , Q P,Q P,Q,所有 i i i 点向 P P P 点连 ( 1 , 0 ) (1,0) (1,0), P , Q P,Q P,Q 连 ( K − L , 0 ) (K-L,0) (K−L,0),然后 Q Q Q 向所有 i ′ i' i′ 连 ( 1 , 0 ) (1,0) (1,0)。
建图到这里才真正结束,容易发现这样的最大费用最大流就是我们要的答案。
这张图的形式看上去非常简单,考虑模拟费用流。
考虑EK的执行过程,我们每次找到当前可行的最大权值增广,然后维护接下来的选择即可。
凭感觉我们知道关键在于怎么处理 P Q PQ PQ 边,显然能不走 P Q PQ PQ 边的都尽量不要走。
于是很容易得到这样的贪心:
每次操作完之后,尽可能在 P Q PQ PQ 边上退流即可。
容易发现维护五个可删堆即可,其实由于删除的条件都比较苛刻,所以可删堆可以不用显式维护。我写的是显式维护可删堆。
代码:
#include
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}
}using namespace IO;
using std::cerr;
using std::cout;
cs int N=2e5+7;
cs int INF=2e9;
struct nd{int vl,id;};
bool operator<(cs nd &a,cs nd &b){
return a.vl<b.vl||(a.vl==b.vl&&a.id<b.id);
}
bool operator==(cs nd &a,cs nd &b){
return a.vl==b.vl&&a.id==b.id;
}
#define sm(i) {a[i]+b[i],i}
#define A(i) {a[i],i}
#define B(i) {b[i],i}
typedef std::priority_queue<nd> heap;
struct Heap{
heap nw,dl;
void adj(){
while(!dl.empty()&&dl.top()==nw.top())
nw.pop(),dl.pop();
}
void push(cs nd &a){nw.push(a);}
void del(cs nd &a){dl.push(a);}
void pop(){adj();nw.pop();}
nd top(){adj();return nw.top();}
bool empty(){return adj(),nw.empty();}
};
int n,K,L,ava;
int a[N],b[N];
bool ai[N],bi[N];
void Main(){
n=gi(),K=gi(),L=gi();ava=K-L;
for(int re i=1;i<=n;++i)a[i]=gi();
for(int re i=1;i<=n;++i)b[i]=gi();
memset(ai+1,0,sizeof(bool)*n);
memset(bi+1,0,sizeof(bool)*n);
Heap S,avA,avB,sgA,sgB;
for(int re i=1;i<=n;++i){
S.push(sm(i));
avA.push(A(i));
avB.push(B(i));
}ll ans=0;
while(K--){
if(ava){
nd ta=avA.top();
nd tb=avB.top();
avA.pop();avB.pop();
ans+=ta.vl+tb.vl;
ai[ta.id]=true;
bi[tb.id]=true;
if(ta.id!=tb.id){
--ava;
if(bi[ta.id]){
++ava;sgA.del(A(ta.id));
}else {
sgB.push(B(ta.id));
S.del(sm(ta.id));
}
if(ai[tb.id]){
++ava;sgB.del(B(tb.id));
}else {
sgA.push(A(tb.id));
S.del(sm(tb.id));
}
}else S.del(sm(ta.id));
continue;
}int vs=-INF,va=-INF,vb=-INF;
if(!S.empty())vs=S.top().vl;
if(!sgB.empty())va=avA.top().vl+sgB.top().vl;
if(!sgA.empty())vb=avB.top().vl+sgA.top().vl;
int mx=std::max({vs,va,vb});ans+=mx;
if(mx==vs){
int id=S.top().id;S.pop();
ai[id]=bi[id]=true;
avA.del(A(id));avB.del(B(id));
}else if(mx==va){
int id=sgB.top().id;sgB.pop();
bi[id]=true;avB.del(B(id));
id=avA.top().id;avA.pop();ai[id]=true;
if(bi[id]){
++ava;sgA.del(A(id));
}else {
sgB.push(B(id));
S.del(sm(id));
}
}else {
int id=sgA.top().id;sgA.pop();
ai[id]=true;avA.del(A(id));
id=avB.top().id;avB.pop();bi[id]=true;
if(ai[id]){
++ava;sgB.del(B(id));
}else {
sgA.push(A(id));
S.del(sm(id));
}
}
}cout<<ans<<"\n";
}
inline void file(){
#ifdef zxyoi
freopen("sequence.in","r",stdin);
#endif
}signed main(){file();int T=gi();while(T--)Main();return 0;}