题意
重组给定的正整数序列,使 ∑ i = 1 n − 1 a i a i + 1 \sum_{i=1}^{n-1} a_ia_{i+1}\qquad ∑i=1n−1aiai+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;
}