题解:P16534 [THUPC 2026 决赛] 年鉴整理
lailai0916 · · 题解
题意简述
每次可把相邻的后一本年鉴移到前面,并令它的破损度增加
解题思路
一次操作会把相邻两项
在同一位置连续操作
因此,
对连续三项依次操作右、左位置,并重复
这个过程使用
下面从左到右固定答案。设当前尚未固定的后缀为
选择使
接下来,需要保证后缀中无论哪一项被选作下一个固定项,其最终值都严格大于
先考虑原位置
由于
再考虑原位置
由
当
当
因此,每固定一项后,下一项一定可以取得更大的值。重复这一过程,直到只剩最后
还需证明操作数不超过限制。设当前后缀长度为
当
最后
若两项关键量相等,则让靠后的项先被选中。其余项经过位置变化后,下一轮的最小关键量至少增加
随后对这四项的相对位置依次操作:
序列会变为
当原序列长度不超过
每组数据的时间复杂度为
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N=505;
const int M=250005;
int n,cnt;
int a[N],op[M];
void apply(int p)
{
a[p+1]++;
swap(a[p],a[p+1]);
op[++cnt]=p;
}
void rollback(int p)
{
cnt--;
swap(a[p],a[p+1]);
a[p+1]--;
}
bool sorted(int l)
{
for(int i=l+1;i<=n;i++)if(a[i-1]>=a[i])return 0;
return 1;
}
bool search(int l,int dep)
{
if(sorted(l))return 1;
if(!dep)return 0;
for(int i=l;i<n;i++)
{
apply(i);
if(search(l,dep-1))return 1;
rollback(i);
}
return 0;
}
void finish(int l)
{
int len=n-l+1;
for(int i=0;i<=len*(len-1);i++)if(search(l,i))return;
cnt=-1;
}
void add2(int p)
{
for(int i=1;i<=4;i++)apply(p);
}
void add3(int p)
{
for(int i=1;i<=3;i++)
{
apply(p+1);
apply(p);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
cnt=0;
if(n<=4)finish(1);
else
{
for(int i=1;i<=n-4;i++)
{
int p=i;
for(int j=i+1;j<=n;j++)if(a[j]+j<a[p]+p)p=j;
for(int j=p-1;j>=i;j--)apply(j);
if(p<n)
{
if(p==n-1)add2(p);
else
{
int j=p+1;
while(n-j+1>3)
{
add2(j);
j+=2;
}
if(n-j+1==2)add2(j);
else if(n-j+1==3)add3(j);
}
}
}
finish(n-3);
}
if(cnt==-1)
{
cout<<-1<<'\n';
continue;
}
cout<<cnt<<'\n';
for(int i=1;i<=cnt;i++)cout<<op[i]<<(i==cnt?'\n':' ');
if(!cnt)cout<<'\n';
}
return 0;
}