CF1984G Magic Trick II 题解
前记
第一篇黑题题解。难调。好写。码量不大。
Description
给定一个大小为 的排列 ,选择一个 ,对 执行操作若干次使得 。
每次操作有两个参数 表示将从 开始的连续 个数从 中取出,再插入此时 中第 和第 个数之间。
如,, 取 ,依次执行操作 、:
将 取出,此时 ,在将 放在第 个数和第 个数之间(即开头),操作完成后 。
再执行 ,。
Solution
结论:。
可通过打表瞎猜得出。
实际上不一定要得出这个结论,因为题目要求最大化 ,所以 从 依次递减,越早能构造出越好。
下面证明并构造。
时
此时序列无论如何操作都不会变,所以要求 一开始就符合条件。
时
此时序列每次操作等于循环移一位,要求 一开始在循环移若干位后能满足条件。
时
先考虑 为奇数的情况。
每一次执行 相当于循环移 位,由于 为奇数,每个数所在下标奇偶性会改变,所以在执行 此操作以内能使序列循环移位任意长度。
-
先把 (初始为 )移到第 位,操作为
-
在后 个数中不断移位使 在第 位,操作为 。
-
循环移一次,使得 接在 的后面,操作为 。
这样 和 就有序了,并且之后不会再分开。
然后 ,重复以上步骤直至 时停止。
再考虑 为偶数的情况。
每一次执行 也相当于循环移 位,但无法改变下标奇偶性,所以在上面操作 中, 可能无法移到第 位,但一定能移到第 或 位,因为 和 奇偶性不同。
如果能移到第 位,就同上操作,否则移到第 位,再在前 个数中循环移位直到第 位为 ,操作为 ,注意此时不再需要操作 。
考虑 到 已排好序,此时有六种情况:
第 种能移位成第 种,第 种通过操作 变为第 种。
另外 种无论如何都无法变为第 种。
为什么?
对于操作 ,若 会与 相对位置发生变化,设原来有 对逆序对,操作后则会有 对逆序对,发现逆序对数奇偶性不变,因为 为偶数, 也就变为偶数。
因此每次操作无法改变序列内逆序对个数。
若初始时 就有奇数个逆序对,则最终总会有 个逆序对不会被消去。
那么此时 才能解决问题。
时
先将 移到第 位。
这里的操作较特殊。
设 表示 所在下标。
若 ,则将以 为结尾的长度为 的段放在末尾即可,操作为 。
若 ,则将以 为开头的长度为 的段放在末尾,操作为 ,即循环移位 位,直到满足上面的条件并执行上面的操作。
然后使 在前 位排序,可以发现,此时总长 为奇数,且 ,所以将其视为上面 为奇数的情况并做操作即可。
注意下面实现时,每次操作暴力改变是 的,但实际上可以用 的链表或其他东西维护,使其总时间复杂度降为 ,但由于 次操作严格跑不满,所以 也能过。
Code
#include<bits/stdc++.h>
using namespace std;
int t;
int n,k,cnt,now;
int a[1010],b[1010];
int ans[5000500][2];
void alter(int x,int y){
int s=1;
ans[++cnt][0]=x,ans[cnt][1]=y;
for(int i=1;i<y;i++){
if(s>=x&&s<=x+k) s=x+k;
b[i]=a[s++];
}
for(int i=y;i<=y+k-1;i++){
b[i]=a[x+i-y];
}
for(int i=y+k;i<=n;i++){
if(s>=x&&s<=x+k) s=x+k;
b[i]=a[s++];
}
for(int i=1;i<=n;i++){
a[i]=b[i];
}
}
int find(int x){
for(int i=1;i<=n;i++){
if(a[i]==x) return i;
}
}
void print(){
cout<<cnt<<'\n';
for(int i=1;i<=cnt;i++){
cout<<ans[i][0]<<" "<<ans[i][1]<<'\n';
}
}
void solve(){
cin>>n;
cnt=0;
int cnt1=0,cnt2=0;
cin>>a[1];
for(int i=2;i<=n;i++){
cin>>a[i];
if(a[i]!=a[i-1]+1) cnt2++;
for(int j=1;j<i;j++){
if(a[i]<a[j]) cnt1++;
}
}
if(cnt2==0){
cout<<n<<'\n'<<0<<'\n';
return ;
}
if(cnt2==1){
k=n-1;
cout<<k<<'\n';
while(a[1]!=1){
alter(2,1);
}
print();
return ;
}
if(n%2==1){
k=n-2,now=2;
cout<<k<<'\n';
while(now<=n){
while(a[1]!=now){
alter(3,1);
}
while(a[n]!=now-1){
alter(3,2);
}
alter(3,1);
now++;
}
while(a[1]!=1){
alter(3,1);
}
print();
return ;
}
if(cnt1%2==0){
k=n-2,now=2;
cout<<k<<'\n';
int type;
while(now<=n){
type=find(now)%2;
if(type==0){
while(a[n]!=now){
alter(3,1);
}
while(a[n-1]!=now-1){
alter(2,1);
}
now++;
}else{
while(a[1]!=now){
alter(3,1);
}
while(a[n]!=now-1){
alter(3,2);
}
alter(3,1);
now++;
}
}
while(a[1]!=1){
alter(3,1);
}
print();
return ;
}else{
k=n-3,now=2;
cout<<k<<'\n';
while(a[n]!=n){
int x=find(n);
if(x<n-3){
alter(1,4);
}else{
alter(x-(n-3)+1,4);
}
}
while(now<=n-1){
while(a[1]!=now){
alter(3,1);
}
while(a[n-1]!=now-1){
alter(3,2);
}
while(a[1]!=1){
alter(3,1);
}
now++;
}
print();
return ;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>t;
while(t--){
solve();
}
return 0;
}
本文作者:larryyu_blog
本文链接:https://www.cnblogs.com/larryyu/p/18351915
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步