BZOJ 3180 coci2012 ograda

题目描述

给出长度为N的序列和N个数字,要求把给出的N个数重新排成一个数列,使新数列与原数列相似且相邻两个数的差的和最大。相似的定义:B数列中第i个数与相邻的数的大小关系与A数列中的第i个数与相邻的数的大小关系一样。

输入

第一行N代表数列的长度

第二行N个数,代表给出的序列

第三行N个数,代表给出的数

输出

输出共两行。第一行为相邻的数的差的和,第二行N个数,代表新数列

数据规模与约定

N<=300000,数列中的数<=10^9

思路

这题看起来较复杂,其实比较简单。把这个数列用点表示出来(单调上升或下降区间视为一个点),则整个数列画出来的样子大概是这样的(一凹一凸):


我们可以发现答案至于这些虚线的顶点有关,相邻两个顶点之间的点是不会影响答案的,所以只要保证两个顶点间的差值最大即可。所以我们把给出的数字排序,记录两个端点L,R代表数组两端下一个取出来的是多少。先找出所有顶点,再按大小插入即可,注意边界情况。

CODE:

#include<cstdio>
#include<algorithm>
using namespace std;
class Dread{
	public:
		int Int()            { int x;read(x);return x; }
	private:
		bool isdigit(char ch){ return ('0'<=ch && ch<='9'); }
		void read(int &x){
			char ch;x=0;
			while (ch=getchar()) if (isdigit(ch)) break;
			for (; isdigit(ch); ch=getchar()) x=x*10+ch-'0';
		}
}READ;
const int N_MAX=300000+10;
int a[N_MAX],b[N_MAX],ans[N_MAX];
inline int abs(int x){ return x<0?-x:x; }
int main(){
	int N=READ.Int();
	for (int i=1; i<=N; ++i) a[i]=READ.Int();
	for (int i=1; i<=N; ++i) b[i]=READ.Int();
	sort(b+1,b+N+1);
	int L=1,R=N;
	for (int i=2; i<N; ++i){
		if (a[i-1]<a[i] && a[i]>a[i+1]) ans[i]=b[R--];
		if (a[i-1]>a[i] && a[i]<a[i+1]) ans[i]=b[L++];
	}
	ans[1]=a[1]<a[2]?b[L++]:b[R--];
	ans[N]=a[N]<a[N-1]?b[L++]:b[R--];
	for (int i=2; i<N; ++i){
		if (a[i-1]<a[i] && a[i]<a[i+1]) ans[i]=b[L++];
		if (a[i-1]>a[i] && a[i]>a[i+1]) ans[i]=b[R--];
	}
	long long res=0;
	for (int i=2; i<=N; ++i) res+=abs(ans[i]-ans[i-1]);
	printf("%lld\n", res);
	for (int i=1; i<=N; ++i) printf("%d ", ans[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值