CF1601C Optimal Insertion 题解


题意

给你一个长度为\(n\)的数组\(a\)和一个长度为\(m\)的数组\(b\),你要保证\(a\)中的元素相对位置不变,任意把\(b\)中元素插入到\(a\)中,最后得到一个长度为\(n+m\)的数组\(c\),请你最小化这个得到的数组的逆序对数。

分析

首先发现一个显然的结论,对于\(b\)数组,我们一定是要把这个数组从小到大排序后以此插入到\(a\)数组中最优。这里可以发现,因为这个\(b\)数组的插入顺序是有序的,所以对于整个\(b\)数组,先把这个数组进行从小到大的排序,然后假设我们确定了一个位置\(i\)应该插入到\(a\)数组中的哪个位置,我们把这个位置记为\(p_i\),那么对于所有小于\(i\)的位置,我们都要把它们插入到这个位置的左边,而对于所有大于\(i\)到位置,我们都要把它们插入到这个位置的右边,然后我们发现,剩下的问题就是我们要把\(b_1\)\(b_{i-1}\)插入到区间\([1,p_i)\)\(b_{i+1}\)\(b_n\)插入到区间\((p_i,n]\),而对于这两个区间,我们也可以进行相同的操作,所以我们可以考虑分治做法解决即可。
我们记一个函数\(f(l,r,L,R)\),前两个参数表示\(b\)数组的区间,后两个代表\(a\)数组的区间,然后我们对于每个区间都\(O(n)\)的扫一遍找一个逆序对数的最小值,把这个最小值的位置记录下来表示这里的\(b_{mid}\)应该插入的位置\(p_{mid}\),通过一个计算区间和的数据结构算出对答案逆序对的贡献,然后再分治\(f(l,mid,L,p_{mid})\)和区间\(f(mid+1,r,p_{mid}+1,R)\)即可。
但是,我们发现这个数据结构貌似很难去实现,所以我们考虑另一种实现方式,我们发现,我们要插入一个位置\(p\)使得插入这个位置后对所有的位置的逆序对贡献最小,但是我们考虑,我们只能在\([l,r]\)区间中插入,那么无论我们怎么插入这个数,我们都不能改变那些\(1 \leq i \leq L\)以及\(R \leq i \leq n\)的位置对逆序对的贡献,我们能改变的只有\([l,r]\)这段区间里的逆序对,换句话说,我们只需要使得我们插入的位置满足在这个区间中提供的逆序对的个数最小即可,这个东西在我们每次分治区间时可以\(O(n)\)的来解决。

代码

咕咕会补的
才不是懒得写


/* Creat on Xishui's iPad 
    Author: Xishui
    date: 21/10/27
    Language: C++
*/

#include <bits/stdc++.h>
using namespace std;
int n,m;
const int N=1e6+10;
int a[N],b[N],pos[N];
void slove(int ql,int qr,int l, int r){
    if(ql>qr||l>r)
        return;
    //printf ( "%d %d %d %d\n",ql,qr,l,r);
    int res=2147483647;
    int inv=0,sign;
    int mid=(ql+qr)>>1;
    for(int i=l;i<=r;i++)
        if(a[i]<b[mid])
            inv++;
    for(int i=l;i<=r;i++){
        if(a[i]>b[mid])
            inv++;
        if(a[i]<b[mid])
            inv--;
        if(inv<res){
            res=inv;
            sign=i;
        }
    }
    pos[mid]=sign;
    slove(ql,mid-1,l,sign);
    slove(mid+1,qr,sign,r);
    return;
}
int c[N<<1],tem[N<<1];
long long ans=0;
void merge_sort(int l, int r){
    if(l==r)
        return;
    int mid=(l+r)>>1;
    merge_sort(l,mid);
    merge_sort(mid+1,r);
    int lz=l,rz=mid+1,cnt=l-1;
    while(lz<=mid&&rz<=r){
        if(c[lz]<=c[rz])
            tem[++cnt]=c[lz++];
        else {
            tem[++cnt]=c[rz++];
            ans+=mid-lz+1;
        }
    }
    lz--,rz--;
    while(lz<mid)
        tem[++cnt]=c[++lz];
    while(rz<r)
        tem[++cnt]=c[++rz];
    for(int i=l;i<=r;i++)
        c[i]=tem[i];
    return;
}
int main(void){
    int T;
    scanf("%d",&T);
    while(T--){
        ans=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int j=1;j<=m;j++)
            scanf("%d",&b[j]);
        sort(b+1,b+m+1);
        slove(1,m,0,n);
        //for(int i=1;i<=m;i++)
        //    printf("%d ",pos[i]);
        //puts("");
        int lenc=0;
        for(int i=0,j=1;i<=n+1;i++){
            while(pos[j]==i-1&&j<=m)
                c[++lenc]=b[j++];
            c[++lenc]=a[i];
        }
        lenc--;
        merge_sort(1,lenc);
        printf("%lld\n",ans);
    }
    return 0;
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM