zoukankan      html  css  js  c++  java
  • 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 2cdot 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;
    }
    
  • 相关阅读:
    ftp如果有文件夹直接建文件,没有创建文件夹
    jfinal相关
    jfinal多数据源
    创建maven项目多模块项目
    多线程分批处理list内的值
    内部类_常见的用途
    获取aplicationContext对象,从而获取任何注入的对象
    jquery表单验证
    monkeyrunner录制回放
    android稳定性测试
  • 原文地址:https://www.cnblogs.com/withhope/p/11762259.html
Copyright © 2011-2022 走看看