Luogu

LOJ

分析

至少有 $L$ 个下标相同 $\Longleftrightarrow$ 至多有 $K-L$ 个下标不同。

考虑一个费用流模型,即从源点向 $a$ 中所有点连容量为 $1$、费用为 $a_i$ 的边,从 $b$ 中所有点向汇点连容量为 $1$、费用为 $b_i$ 的边,从 $a_i$ 向 $b_i$ 连容量为 $1$、费用为 $0$ 的边,再新建两个点 $C,D$,从 $C$ 向 $D$ 连容量为 $K-L$、费用为 $0$ 的边,从 $a$ 中所有点向 $C$ 连边,从 $D$ 向 $b$ 中所有点连边。这样子求流量为 $K$ 时的最大费用即为答案。

考虑模拟费用流。显然我们会优先流 $C\to D$,因为此时可能的收益最大。当 $C\to D$ 这条边满流时我们有如下选择:

  • 选一个 $a_i+b_i$。
  • 选一个 $b_i$ 已经匹配了的 $a_i$ 和 $b_j$ 匹配。
  • 选一个 $a_i$ 已经匹配了的 $b_i$ 和 $a_j$ 匹配。

于是我们开五个堆,分别维护未匹配的 $a_i$ 的最大值、未匹配的 $b_i$ 的最大值、未匹配的 $a_i+b_i$ 的最大值、$b_i$ 已经匹配了的 $a_i$ 的最大值、$a_i$ 已经匹配了的 $b_i$ 的最大值,每次三种情况比较一下即可。

实现时需要维护 $C\to D$ 弧的残量已经每个 $a_i$、$b_i$ 是否已经匹配,写起来需要注意各种不会减少(甚至可能增加) $C\to D$ 弧残量的情况。

代码

// ===================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ===================================
#include <bits/stdc++.h>
#define mp make_pair
#define fi(x) (x.first)
#define se(x) (x.second)
using namespace std;
typedef long long ll;

int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=200000+10;

int n,K,L,a[N],b[N],vA[N],vB[N];
priority_queue<pair<int,int> > QA,QB,QAB,FA,FB;

int main() {
    int 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();

        while (!QA.empty()) QA.pop();
        while (!QB.empty()) QB.pop();
        while (!QAB.empty()) QAB.pop();
        while (!FA.empty()) FA.pop();
        while (!FB.empty()) FB.pop();
        for (int i=1;i<=n;++i) vA[i]=vB[i]=0;

        for (int i=1;i<=n;++i)
            QA.push(mp(a[i],i)),QB.push(mp(b[i],i)),QAB.push(mp(a[i]+b[i],i));
        ll ans=0,cap=K-L;
        for (int f=1;f<=K;++f) {
            while (!QA.empty()&&vA[se(QA.top())]) QA.pop();
            while (!QB.empty()&&vB[se(QB.top())]) QB.pop();
            if (cap) {
                int i=se(QA.top()),j=se(QB.top()); QA.pop(),QB.pop();
                --cap; ans+=a[i]+b[j]; vA[i]=vB[j]=1;
                if (i==j) ++cap,QAB.pop();
                else {
                    if (vB[i]) ++cap,FA.pop();
                    else FB.push(mp(b[i],i));
                    if (vA[j]) ++cap,FB.pop();
                    else FA.push(mp(a[j],j));
                }
            } else {
                auto QABt=QAB.empty()?mp(0,0):QAB.top();
                while (!QAB.empty()&&(vA[se(QABt)]||vB[se(QABt)]))
                    QAB.pop(),QABt=QAB.empty()?mp(0,0):QAB.top();
                auto QAt=QA.empty()?mp(0,0):QA.top();
                auto QBt=QB.empty()?mp(0,0):QB.top();
                auto FAt=FA.empty()?mp(-fi(QBt),0):FA.top();
                auto FBt=FB.empty()?mp(-fi(QAt),0):FB.top();
                int wAB=fi(QABt),wA=fi(FAt)+fi(QBt),wB=fi(QAt)+fi(FBt);
                if (wAB>=wA&&wAB>=wB) {
                    ans+=wAB,vA[se(QABt)]=vB[se(QABt)]=1;
                } else if (wA>=wB) {
                    ans+=wA,vA[se(FAt)]=vB[se(QBt)]=1,FA.pop(),QB.pop();
                    if (vA[se(QBt)]) FB.pop(),++cap;
                    else FA.push(mp(a[se(QBt)],se(QBt)));
                } else {
                    ans+=wB,vA[se(QAt)]=vB[se(FBt)]=1,FB.pop(),QA.pop();
                    if (vB[se(QAt)]) FA.pop(),++cap;
                    else FB.push(mp(b[se(QAt)],se(QAt)));
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
最后修改:2020 年 05 月 28 日 08 : 09 PM