[NOI2019] 序列

题面

题面

得分情况

100pts: 6人
>=84pts: 11人
>=64pts: 23人

题解

20pts: n ≤ 18 n\leq 18 n18

暴力枚举选哪 L L L个作为都选的,剩余的 K − L K-L KL个贪心选取,用set即可。

28pts: n ≤ 30 n\leq 30 n30

DP,记录第一个数组选的个数,第二个数组选的个数,和公共的个数,进行转移。

时间复杂度: O ( n 4 ) O(n^4) O(n4)

40pts idea1: n ≤ 150 n\leq 150 n150

将所有下标按照 a i + b i a_i+b_i ai+bi排序。
枚举第 L L L个公共下标的位置 x x x,我们有如下结论:
对于前 x x x个下标,最优解不存在一个下标在两个数组中都没有选
结论显然,若存在,完全可以把一个公共下标放在这个位置,答案会更优。
于是一定存在一个 y y y,使得前 x x x个位置中 a a a恰好有 y y y个没选, b b b恰好有 x − L − y x-L-y xLy个没选。后 n − x n-x nx个位置中 a a a恰好有 K − ( x − y ) K-(x-y) K(xy)个选了, b b b恰好有 K − L − y K-L-y KLy个选了。
那么我们就可以dp求出每个前缀恰好 a a a u u u个没选, b b b v v v个没选的答案,以及后缀 a a a u u u个选了 b b b v v v个选了的答案。
实际上枚举的这个 x x x时一个公共位置的上界

时间复杂度: O ( n 3 ) O(n^3) O(n3)

64pts idea1: n ≤ 2000 n\leq 2000 n2000

在上述40pts的做法下继续思考。
存在一个 y y y,使得前 x x x个位置中 a a a恰好有 y y y个没选, b b b恰好有 x − L − y x-L-y xLy个没选。后 n − x n-x nx个位置中 a a a恰好有 K − x + y K-x+y Kx+y个位置选了, b b b恰好有 K − L − y K-L-y KLy个位置选了。
有一种贪心的想法:选 a a a的时候,前面最小的 y y y个不选,后面最大的 K − x + y K-x+y Kx+y个加上;选 b b b的时候同理,前面最小的 x − L − y x-L-y xLy个不选,后面最大的 K − L − y K-L-y KLy个加上。
显然有个小问题: a a a b b b可能同时删掉了某个下标,这样前 x x x个位置就有大于 L L L个公共的了。
但是,如果 a a a b b b同时没选某个下标,这个方案会比所有 a a a b b b没有同时没选某个下标的方案优,且这个方案显然不是最优,那么最终答案的 x x x肯定不是现在的枚举的这个 x x x
于是预处理出前缀和后缀各自的前 x x x大/小和,贪心的做即可。

时间复杂度: O ( n 2 ) O(n^2) O(n2)

84pts idea1: ∑ n ≤ 3 e 5 \sum n\leq 3e5 n3e5

a a a前面最小的 y y y个删掉,后面最大的 K − x + y K-x+y Kx+y个加上, b b b选前面最小的 x − L − y x-L-y xLy个删掉,后面最大的 K − L − y K-L-y KLy个加上。
我们设 a 0 a0 a0 a a a前面 x x x个元素的集合, a 1 a1 a1 a a a后面 n − x n-x nx个元素的集合, b 0 , b 1 b0,b1 b0,b1同理。
一个不同的思考方式是,进行 K − L K-L KL次操作。我们每次可以:

  1. a 0 a0 a0的最小元素删掉, a 1 a1 a1的最大元素加上。
  2. b 0 b0 b0的最小元素删掉, b 1 b1 b1的最大元素加上。

我们可以用数据结构对任意 u u u次计算出第 u u u次操作 1 1 1或操作 2 2 2的增量。
不妨假设最后一次我们进行的是操作 1 1 1,那么这意味着进行一次操作 1 1 1的增量比操作 2 2 2的增量要大,反之亦然。
于是,假设进行了 y y y次操作 1 1 1, K − L − y K-L-y KLy次操作 2 2 2,那么第 K − L − y + 1 K-L-y+1 KLy+1次操作 2 2 2的代价比第 y y y次操作 1 1 1的代价要大。注意到这样的 y y y是可二分的,也就是说如果第 y y y次操作 1 1 1的代价比第 K − L − y + 1 K-L-y+1 KLy+1次操作 2 2 2的代价要小,那么对于所有 y ′ > y y'>y y>y,第 y y y次操作 1 1 1的代价也会比第 K − L − y ′ + 1 K-L-y'+1 KLy+1次操作 2 2 2的代价要小。
于是可以二分这个 y y y,使用支持加入和查询第 k k k大的数据结构维护即可做到 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

100pts idea1:

引理:对于 i i i j j j,设 x = i x=i x=i时的最优解(为了方便讨论假设最优解唯一)中,删除的下标集合为 S i S_i Si,对于 x = j x=j x=j时的下标集合为 S j S_j Sj,则 S i S_i Si S j S_j Sj的子集。
证明:

假设 x = i x=i x=i时的最优解存在一个下标 u u u被删除,但是在 x = j x=j x=j时该下标未被删除。
不妨假设这个下标在 a a a中,且 b b b j j j删除的元素个数不比 i i i删除的元素个数少。
那么我们可以尝试找一个 v ≤ i v\leq i vi,使得 i i i v v v没有被删除,但是在 j j j中被删除,然后我们可以在 j j j中用 a v a_v av替换 a u a_u au,这个替换一定不会使解变劣,否则我们可以在 i i i中进行逆操作使得 i i i的解变优。
假设找不到这样的 v ≤ i v\leq i vi,这说明在前 i i i个元素中 i i i中删除的元素时 j j j的真子集,我们在 b b b中尝试找一个下标 l ≤ i l\leq i li使得它在 i i i中没被删除而在 j j j中被删除,如果这个下标也找不到,那么 S i S_i Si S j S_j Sj的子集,这意味着我们 i i i的方案本身就符合 j j j的条件,所以 x = j x=j x=j时我们可以直接用 x = i x=i x=i的答案。
我们再找一个下标 m > i m>i m>i使得它在 i i i中没被选而在 j j j中被选,根据 b b b j j j删除的元素个数不比 i i i删除的元素个数少的假设我们也能证明这个 m m m一定能找到。
于是我们可以用 a v a_v av替换 a u a_u au b l b_l bl替换 b m b_m bm,同样的道理可以证明这个替换不会使解变劣。

我们根据引理容易发现这个 y y y时单调的,而且如果当前枚举的 y y y小于正确答案的 y y y,那么我们直接将 y + 1 y+1 y+1答案不会变劣(这是因为 84 84 84分做法里的单调性)。
所以我们将二分改成双指针就可以将时间复杂度变为 O ( n l o g n ) O(nlogn) O(nlogn)
但是我们注意到,由于 a a a选前面最小的 y y y个删掉, b b b选前面最小的 x − L − y x-L-y xLy个删掉,如果 x x x加一,为了保证两个数都不减, y y y只能不变或者加一。
也就是说,每一步我们的可能性只有两种: y y y加一或者不加。我们可以想一想这个做法是在干什么。
对于 x = K x=K x=K,我们将和是前 L L L大的下标都选上,然后在后面 n − K n-K nK个下标中, a a a b b b都各自选最大的 K − L K-L KL个。
那么如果 x x x加一,思考一下 y y y加一或者不加分别对应哪些操作。

  1. x x x这个下标在 a a a b b b两个数组中都选了:
    操作1:将 a 0 a0 a0中的最小值删掉, a 1 a1 a1中没选的最大值加进来。
    操作2:将 b 0 b0 b0中的最小值删掉, b 1 b1 b1中没选的最大值加进来。
  2. x x x这个下标只在一个数组中选了(不妨假设是 a a a):
    操作1:将 a 0 a0 a0中的最小值删掉, a 1 a1 a1中没选的最大值加进来, b 1 b1 b1中选了的最小值删掉。
    操作2:将 b 0 b0 b0中的最小值删掉。
  3. x x x这个下标在两个中都没选:
    操作1:将 a 0 a0 a0中的最小值删掉, b 1 b1 b1中选了的最小值删掉。
    操作2:将 b 0 b0 b0中的最小值删掉, a 1 a1 a1中选了的最小值删掉。

于是使用 6 6 6个优先队列即可解决这个问题。

64~84pts idea2:

注意到对于固定的 L L L,答案关于 K − L K-L KL是凸的,因此可以使用wqs二分
二分一个权值 w w w,限制公共下标数量 L L L但不限制 K K K,然后对于除了公共下标外每个多选的下标都给一个 w w w的额外代价,二分 w w w使最优解恰好为 K K K
固定 w w w时,可以贪心求解。

40~64pts idea3: n ≤ 30 → n ≤ 2000 n\leq 30\rightarrow n\leq 2000 n30n2000

考虑费用流模型。
至少 L L L个下表相同,等价于至多有 K − L K-L KL个下标不同。

对于每个 i i i:
连边 S → a i S\rightarrow a_i Sai,容量为 1 1 1,边权为 a i a_i ai
连边 b i → T b_i\rightarrow T biT,容量为 1 1 1,边权为 b i b_i bi
连边 a i → b i a_i\rightarrow b_i aibi,容量为 1 1 1,边权为 0 0 0,表示选择一对相同的下标。
新建点 U U U, V V V,连边 U → V U\rightarrow V UV,容量为 K − L K-L KL(称为自由流量),边权为 0 0 0,表示选择一对不同的下标。
对每个 i i i,连边 a i → U a_i\rightarrow U aiU, V → b i V\rightarrow b_i Vbi
注意限制总流量为 K K K

在这里插入图片描述
跑最大费用最大流即可。

100pts idea3:

考虑模拟费用流:
观察算法流程,所有和 S S S, T T T直接相连的边不会退流,所以可以忽略这些边的反向边,而中间的边会退流,对应每一对的添加操作。
观察增光路有多少形态:
在这里插入图片描述
假设某次选择下标对 ( a i , b j ) (a_i,b_j) (ai,bj),
其中分别对应以下 5 5 5种方案:
1. i = j i=j i=j
2. i ≠ j i\not=j i=j,消耗 1 1 1自由流量。
3.使 a i a_i ai b i b_i bi(先前已选)配对,将 b j b_j bj a j a_j aj(先前已选)配对,增加 1 1 1自由流量。
4.使 a i a_i ai b i b_i bi(先前已选)配对, b j b_j bj与先前与 b i b_i bi配对的 a x a_x ax配对。
5.使 b i b_i bi a i a_i ai(先前已选)配对, a j a_j aj与先前与 a i a_i ai配对的 b x b_x bx配对。
(其实图中还存在其他形式的增光路,但显然存在更优的形式,故只考虑这5种即可)

考虑怎么维护以上 5 5 5种方案。
可以用五个堆 q , q a , q b , q a 2 , q b 2 q,q_a,q_b,q_{a_2},q_{b_2} q,qa,qb,qa2,qb2,分别表示:
q : q: q:两边都未选的 a i + b i a_i+b_i ai+bi m a x max max
q a : q_a: qa: A A A数组未选的数的 m a x max max q b q_b qb同理。
q a 2 : q_{a_2}: qa2: a i a_i ai未选而 b i b_i bi已选的 a i m a x a_{i_{max}} aimax q b 2 q_{b_2} qb2同理。
若某堆顶不存在则视为 − I N F -INF INF
用两个 b o o l bool bool数组维护某一下标是否被取过。
当取走某一下标时,若另一侧未取,则可以对相应的 q a 2 q_{a_2} qa2 q b 2 q_{b_2} qb2添加元素。
模拟方案的过程,取 m a x max max即可。

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

具体细节参考代码:

#include<bits/stdc++.h>
#define ll long long
#define uit unsigned int
#define mkp make_pair
#define pa pair
#define fir first
#define sec second
using namespace std;
const int N=2e5+10,INF=1e9;
int T,n,K,L,A[N],B[N];bool visa[N],visb[N];
priority_queue<pa<int,int> > q,qa,qb,qa2,qb2;
void seta(int R){
    visa[R]=1;
    if(!visb[R]) qb2.push(mkp(B[R],R));
}
void setb(int R){
    visb[R]=1;
    if(!visa[R]) qa2.push(mkp(A[R],R));
}
void solve(){
    scanf("%d%d%d",&n,&K,&L);int lim=K-L,mx=0,op=0;ll ans=0;
    for(int i=1;i<=n;i++) scanf("%d",&A[i]);
    for(int i=1;i<=n;i++) scanf("%d",&B[i]);
    for(int i=1;i<=n;i++) q.push(mkp(A[i]+B[i],i)),qa.push(mkp(A[i],i)),qb.push(mkp(B[i],i));
    q.push(mkp(-INF,0));qa.push(mkp(-INF,0));qb.push(mkp(-INF,0));qa2.push(mkp(-INF,0));qb2.push(mkp(-INF,0));
    ans=0;
    for(int i=1;i<=K;i++){
        mx=op=0;
        while(visa[q.top().sec]||visb[q.top().sec]) q.pop();
        while(visa[qa.top().sec]) qa.pop();
        while(visb[qb.top().sec]) qb.pop();
        while(visa[qa2.top().sec]) qa2.pop();
        while(visb[qb2.top().sec]) qb2.pop();
        pa<int,int> qnow=q.top(),qanow=qa.top(),qbnow=qb.top(),qa2now=qa2.top(),qb2now=qb2.top();
        mx=qnow.fir;op=1;
        if(qa2now.fir+qb2now.fir>mx){
            mx=qa2now.fir+qb2now.fir;
            op=3;
        }
        if(qa2now.fir+qbnow.fir>mx){
            mx=qa2now.fir+qbnow.fir;
            op=4;
        }
        if(qanow.fir+qb2now.fir>mx){
            mx=qanow.fir+qb2now.fir;
            op=5;
        }
        if(qanow.sec!=qbnow.sec&&lim){
            if(qanow.fir+qbnow.fir>mx){
                mx=qanow.fir+qbnow.fir;
                op=2;
            }
        }
        if(op==1){seta(qnow.sec);setb(qnow.sec);}
        if(op==2){seta(qanow.sec);setb(qbnow.sec);lim--;}
        if(op==3){seta(qa2now.sec);setb(qb2now.sec);lim++;}
        if(op==4){seta(qa2now.sec);setb(qbnow.sec);}
        if(op==5){seta(qanow.sec);setb(qb2now.sec);}
        ans+=mx;
    }
    printf("%lld\n",ans);
    while(!q.empty()) q.pop();
    while(!qa.empty()) qa.pop();
    while(!qb.empty()) qb.pop();
    while(!qa2.empty()) qa2.pop();
    while(!qb2.empty()) qb2.pop();
    for(int i=1;i<=n;i++) visa[i]=visb[i]=0;
}
int main(){
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    scanf("%d",&T);
    while(T--) solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值