题目描述
给出长度为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;
}