JZOJ 4665.数列

题意

重组给定的正整数序列,使 ∑ i = 1 n − 1 a i a i + 1 \sum_{i=1}^{n-1} a_ia_{i+1}\qquad i=1n1aiai+1最大。

思路

根据贪心策略易得,构造使上式最大的数列的策略是将最大的元素放在中间位置,然后从大到小在中间元素的左右各放一个元素。按该做法直接构造数列可以得到数列中无重复元素的部分分。

在有重复元素的数列中,首先必须满足中间元素两边各分布一个重复元素。在满足该条件的情况下,如果该元素的字典序比恰好比它大一点的元素(即排序后 a i a_i ai a i + 1 a_{i+1} ai+1的关系)要小,则排在已分布元素的左侧,反之则放在右侧。

在实践中,我们可以灵活选择枚举方式。本篇题解中,我使用了从两侧向中间枚举的方法,优点是可以在不使用双端队列的情况下节约空间。

code

#include<bits/stdc++.h>
#define ll int
#define R register
#define mod 1234567891
using namespace std;
inline ll read(){
	ll f=0,pa=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')pa=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){f=(f<<3)+(f<<1)+(ch^48);ch=getchar();}
	return f*pa;
}
inline void write(ll x){
	if(x<0)x=-x,putchar('-');
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void writelp(ll x){write(x);putchar(' ');}
inline void writeln(ll x){write(x);putchar('\n');}
ll t,n,a[400005],s,e,pan,pa,ji,ans[400005],las,d[100005];
struct node{
	ll w,id;
}p[100005];
bool cmp(node a,node b){
	if(a.w!=b.w)return a.w<b.w;
	else return a.id<b.id;
}
int main(){
	/*freopen("permutation.in","r",stdin);
	freopen("permutation.out","w",stdout);*/
	t=read();
	while(t--){
		n=read();s=0;e=n+1;
		pa=0;ji=0;
		for(R ll i=1;i<=n;i++)
		p[i].w=read(),p[i].id=i,d[i]=p[i].w;
		sort(p+1,p+n+1,cmp);
		for(R ll i=1;i<=n-1;i++){
			pan=(pan^1);
			if(p[i].w!=p[i+1].w){
				if(ji==0){
					ji=1;
					if(p[i].id<p[i+1].id)
					pan=1,a[++s]=p[i].id;
					else pan=0,a[--e]=p[i].id;
				}
				else if(pan==0) a[--e]=p[i].id;
					 else a[++s]=p[i].id;
			}
			else{
				ll j,ee=e,ss=s;
				for(j=i+1;j<=n;j++)
				if(p[j].w!=p[i].w)
				break;
				j--;ll mini;
				if(j<=n-2)
				mini=min(p[j+1].id,p[j+2].id);
				if(j==n-1)
				mini=p[j+1].id;
				if(j==n)j--,mini=p[n].id,ss=0,ee=0;
				for(R ll k=i;k<=j;k++)
					if(p[k].id<mini) a[++s]=p[k].id;
				for(R ll k=j;k>=i;k--)
					if(p[k].id>mini) a[--e]=p[k].id;
				if(s==ss) a[++s]=a[e++];
				if(e==ee) a[--e]=a[s--];
				i=j;ji=0;
			}
		}
		a[(s+e)/2]=p[n].id;
		for(R ll i=1;i<=n;i++)
		writelp(a[i]);
		cout<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值