【Codeforces 802N/802O】April Fools' Problem

贪心,凸优化。

题目大意:

给定两个长度为 $n$ 的数列 $a_1,a_2,a_3,\ldots,a_n$ 和 $b_1,b_2,b_3,\ldots,b_n$,并给定 $k$,要求选出 $i_1,i_2,i_3,\ldots,i_k$ 和 $j_1,j_2,j_3,\ldots,j_k$,满足:

  • $1\leq i_1\lt i_2\lt i_3\lt \ldots\lt i_k\leq n$,$1\leq j_1\lt j_2\lt j_3\lt \ldots\lt j_k\leq n$。
  • $i_1\leq j_1,i_2\leq j_2,i_3\leq j_3,\ldots,i_k\leq j_k$。
  • 最小化 $a_{i_1}+a_{i_2}+a_{i_3}+\cdots+a_{i_k}+b_{j_1}+b_{j_2}+b_{j_3}+\cdots+b_{j_k}$ 的值。

求最小值。

题解:

考虑没有 $k$ 的限制的时候怎么做。

此时答案显然为 $0$,因为 $a$ 和 $b$ 都是正的。

那如果 $a$ 和 $b$ 有负数呢?

我们有以下贪心做法:

从 $n$ 到 $1$ 考虑每个 $a_i$,此时 $b_{i\sim n}$ 都是可以选的。用一个堆维护 $b_{i\sim n}$ 的最小值。

对于已经选择的 $a_i$ 和 $b_j$ 配对,我们用另一个堆维护 $-a_{i}$ 的最小值。

然后考虑新加入一个 $a_i$,先将 $b_i$ 插入堆中。

此时有三种决策:$a_i$ 和最小的 $b_j$ 配对;用 $a_i$ 替换掉一个 $-a_j$ 最小的 $a_j$;不做任何操作。

我们取最优决策即可。如果 $a_i$ 最后被选,则将 $-a_i$ 插入堆中。

时间复杂度为 $O(n\log n)$。

那么这个有什么用呢?

我们考虑函数 $f(k)$ 表示选择 $k$ 对时的最小值。容易证明这是一个凸函数

那么我们对这个算法使用凸优化即可。

时间复杂度 $O(n\log n\log V)$。

Code:

#include<cstdio>
#include<cctype>
#include<queue>
using namespace std;
typedef long long LL;
const int N=5e5+5;
char buf[(int)1.1e8],*ss=buf;
inline int init(){buf[fread(buf,1,(int)1.1e8-1,stdin)]='\n';fclose(stdin);return 0;}
const int __START__=init();
inline int readint(){
    int d=0,f=0;
    while(!isdigit(*ss))f=*ss++=='-';
    while(isdigit(*ss))d=d*10+(*ss++^'0');
    return f?-d:d;
}
int n,a[N],b[N],k;
LL ans,ANS;
inline bool chk(int x){
    priority_queue<int>p,q;
    ANS=0;
    int cnt=0;
    for(int i=n;i;--i){
        q.push(x-b[i]);
        int s=p.empty()?-(1<<30):p.top(),t=q.top();
        if(s<t){
            if(a[i]<t)++cnt,p.push(a[i]),q.pop(),ANS+=a[i]-t;
        }else if(a[i]<s)p.pop(),p.push(a[i]),ANS+=a[i]-s;
    }
    ANS+=(LL)k*x;
    return cnt<=k;
}
int main(){
    n=readint(),k=readint();
    for(int i=1;i<=n;++i)a[i]=readint();
    for(int i=1;i<=n;++i)b[i]=readint();
    unsigned l=0,r=2e9+1;
    while(l<=r){
        const unsigned mid=l+r>>1;
        if(chk(mid))ans=ANS,l=mid+1;else r=mid-1;
    }
    printf("%lld\n",ans);
    return 0;
}