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