Codeforces 1214F. Employment题解

题目链接:1214F. Employment
题目大意:给你两个数列\(a_1,a_2,\dots,a_n\)以及\(b_1,b_2,\dots,b_n\),求一个1 ~ n排列\(P\),使得\(\sum_{i=1}^{n} |a_i-b_{P_{j}}|\)最小,输出最小值及这个排列。
题解:我们先将\(a\)数组和\(b\)数组进行排序,并且将\(b\)数组扩大三倍(\(b_i-m,b_i,b_i+m\)),可以容易证明最优解一定在一段连续的\(b\)序列中。
那么可以看出\(a\)\(b\)在分配时相对顺序是不会改变的,即对于每一个\(b_j\)~\(b_{j+n-1}\),当答案最优时定然为\(ans=\sum_{i=1}^{n} |a_i-b_{j+i-1}|\)
那么我们就可以很轻松地得到一种\(O(n^2)\)的算法,即每一次枚举\(j\),然后暴力判断即可。
但是对于数据范围\(1 \le n \le 2\cdot 10^5\)来说,这个时间复杂度明显是过不了的,那么考虑对该算法进行优化。
我们可以考虑分类讨论来去掉绝对值,那么\(ans=\sum_{i=1}^n [a_i \ge b_{j-i+1}]\cdot(a_i-b_{j-i+1})+[a_i < b_{j-i+1}]\cdot(b_{j-i+1}-a_i)\)
很明显,\(a_i\)\(b_j\)在一段连续的区间中的贡献为正,一段连续区间的贡献为负,不超过两段连续区间的贡献为0,那么就可以算出断点用差分来做即可。
code:(qwq,细节真多)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define Maxn 200000
struct Node{
    ll a;
    int id;
    friend bool operator <(Node p,Node q){
        if(p.a==q.a){
            return p.id<q.id;
        }
        return p.a<q.a;
    }
}a[Maxn+5],b[Maxn*3+5];
int m,n;
int c[Maxn+5];
ll f[Maxn*3+5];
int mx(int a,int b){
    return a>b?a:b;
}
int mn(int a,int b){
    return a<b?a:b;
}
int main(){
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i].a);
        a[i].id=i;
    }
    for(int i=1;i<=n;i++){
        scanf("%lld",&b[i].a);
        b[i].id=i;
        b[i+n]=b[i];
        b[i+n].a+=m;
        b[i+(n<<1)]=b[i];
        b[i+(n<<1)].a-=m;
    }
    sort(a+1,a+1+n);
    sort(b+1,b+1+n*3);
    ll ans=(1ll<<60),sum;
    int x=0;
    for(int i=1,j=1;i<=n;i++){
        while(b[j].a<=a[i].a){
            j++;
        }
        f[0]+=a[i].a;
        if(j-i+1<=2*n+1){
            f[j-i+1]-=(a[i].a<<1);
        }
    }
    for(int j=1,i=1;j<=n*3;j++){
        f[mx(0,j-n+1)]-=b[j].a;
        while(i<=n&&a[i].a<b[j].a){
            i++;
        }
        if(j-i+2<=n*2+1){
            f[j-i+2]+=(b[j].a<<1);
        }
        f[mn(n*2+2,j+1)]-=b[j].a;
    }
    sum=f[0];
    for(int i=1;i<=2*n+1;i++){
        sum+=f[i];
        if(sum<ans){
            x=i;
            ans=sum;
        }
    }
    for(int i=1;i<=n;i++){
        c[a[i].id]=b[x+i-1].id;
    }
    cout<<ans<<endl;
    for(int i=1;i<=n;i++){
        printf("%d ",c[i]);
    }
    puts("");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/withhope/p/11762259.html