首页 > 其他分享 >洛谷P3516 [POI2011] PRZ-Shift

洛谷P3516 [POI2011] PRZ-Shift

时间:2024-11-07 10:09:03浏览次数:1  
标签:opt int Shift POI2011 pos back num ans P3516

题意

Link

有一个排列 \(a\),你可以执行两种操作:

  • A:将最后一个数移到最前面
  • B:将第三个数移到最前面

构造一组操作序列将其变为递增排列,输出形如 5a 2b ... 表示执行 \(5\) 次 A 操作再执行 \(2\) 次 B 操作。

思路

很有意思的构造。仔细思考,操作 A 使我们能将原排列变为它的任何一个循环同构[1],于是配合操作 B,我们就可以将任意一个数(除最前面两个数)向前移动两步。具体的方法就是假设我们想移的数位置为 \(pos\),我们同构操作 A 将 \((pos-2,pos-1,pos)\) 移到最前面,经过一次操作 B 变为 \((pos,pos-2,pos-1)\),然后再通过操作 A 移回去。

到这里我们已经可以处理所有长度为偶数的移动了,那么奇数呢?我们会发现,假设连续执行两次操作 B,我们就可以将任意一个数(除第一个和最后一个数)向前移动一步。具体还是仿照之前那样将 \((pos-1,pos,pos+1)\) 移到最前面,执行两次 B,就会变成 \((pos,pos+1,pos-1)\),再移回去。

有个问题,移动之后 \(pos-1\) 和 \(pos+1\) 的相对顺序乱了,但这不要紧,我们考虑 \(1,2,\dots,n\) 依次移动每个数的位置,我们只要确保每次操作不影响小于 \(a[pos]\) 的数就行,而当我们需要 \(pos\) 和 \(pos-1\) 交换位置时\(a[pos-1]\) 肯定是大于 \(a[pos]\) 的,因此这不影响。

到了最后两个数时,会有两种情况,第一种是 \(a[n-1]=n-1,a[n]=n\),说明排序好了;另外一种是 \(a[n-1]=n,a[n]=n-1\),但这个时候我们无法用之前的办法来移动一步,因为此时可以动的数少于 \(3\) 个,此时应该用移动两步的方法将 \(n\) 移到最前面,再用 \(n-1\) 次操作 A 将后面的移到前面来,完成排序;但如果 \(n\) 前面有奇数个数,则无法用移动两步的方法移到最前面,说明无解。

代码

不要忘了特判 \(n\leq 2\) 的情况。

另外注意操作 A 次数要模 \(n\),操作 B 次数要模 \(3\)。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int MAXN=2e3+5;
int n,a[MAXN];
vector<pii>opt,ans;
inline void shift1(int pos){
    assert(2<=pos && pos<=n-1);
    int prelen=pos-2;
    if(prelen>0) opt.emplace_back(1,n-prelen);
    opt.emplace_back(2,2);
    if(prelen>0) opt.emplace_back(1,prelen);

    int x=a[pos-1],y=a[pos],z=a[pos+1];
    a[pos-1]=y;a[pos]=z;a[pos+1]=x;
}
inline void shift2(int pos){
    assert(3<=pos);
    int prelen=pos-3;
    if(prelen>0) opt.emplace_back(1,n-prelen);
    opt.emplace_back(2,1);
    if(prelen>0) opt.emplace_back(1,prelen);

    int x=a[pos-2],y=a[pos-1],z=a[pos];
    a[pos-2]=z;a[pos-1]=x;a[pos]=y;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    if(n==1){
        puts("0");
        return 0;
    }
    if(n==2){
        if(a[1]==1 && a[2]==2) puts("0");
        else puts("1\n1a");
        return 0;
    }
    int pos;
    for(int num=1;num<=n-2;num++){
        for(int i=1;i<=n;i++)
            if(a[i]==num){pos=i;break;}
        assert(pos>=num);
        if(pos==num) continue;
        while(pos-num>=2){
            shift2(pos);
            pos-=2;
        }
        if(pos>num){
            shift1(pos);
            pos-=1;
        }
        assert(pos==num);
    }
    if(a[n-1]==n){
        pos=n-1;
        if((pos-1)%2==1){
            puts("NIE DA SIE");
            return 0;
        }
        else{
            while(pos!=1){
                shift2(pos);
                pos-=2;
            }
            opt.emplace_back(1,n-1);
        }
    }
    for(auto it:opt){
        if(ans.empty() || ans.back().first!=it.first) ans.push_back(it);
        else ans.back().second+=it.second;
        if(ans.back().first==1 && ans.back().second>=n){
            ans.back().second%=n;
            if(ans.back().second==0) ans.pop_back();
        }
        else if(ans.back().first==2 && ans.back().second>=3){
            ans.back().second%=3;
            if(ans.back().second==0) ans.pop_back();
        }
    }
    cout<<ans.size()<<endl;
    for(auto it:ans){
        cout<<it.second<<(it.first==1?'a':'b')<<' ';
    }
    return 0;
}

  1. 循环同构:将原序列中间某处断开,前面移到后面或后面移到前面得到的新序列。 ↩︎

标签:opt,int,Shift,POI2011,pos,back,num,ans,P3516
From: https://www.cnblogs.com/SkyNet-PKN/p/18531624

相关文章

  • [ARC084F] XorShift
    模拟赛题。考虑操作的构成,先忽略\(1\)操作,只考虑任意两个数的异或,不难发现所有能构成的数即为线性基。再考虑\(1\)操作,显然可以对开始的每个数率先进行\(1\)操作再构建线性基。记\(lim=\max(\log_2a,\log_2m)\),发现所有可能有效的数都不超过\(2^{2lim}\)。再考......
  • 总结 JavaScript 中8种数组常用的操作 API,array.push,pop,shift,unshift,slice,splice
    前言JavaScript中数组是一个重要的数据结构,它相比于字符串有更多的方法,在一些算法题中我们经常需要将字符串转化为数组,使用数组里面的API进行操作。本篇文章总结了JavaScript中有许多数组常用的操作API,以下是一些常见的操作及其示例:1.push():在数组末尾添加一个或多个元素,并......
  • POI2011/洛谷P3523 DYN-Dynamite
    前言Link本来一个很直观的题面,非要搞形式化题意反而使题意变得非常迷惑。题意有一栋树形建筑,其中有一些点摆放了TNT,树边上都摆放了引信,引信的燃烧时间为\(1\)秒\(/\)边,现在你要选择\(m\)个点同时点燃引信(起爆),则显然TNT被引爆的时间为到离它最近的起爆处的距离,请你求......
  • POI2011/洛谷P3514 LIZ-Lollipop
    前言典中典思维蓝题难度薄纱模板水紫捏。\(1\)\(2\)序列这种也不是第一次见了,感觉多多少少都沾点Ad-hoc。话说这种考法真的好吗,一上来就是一个门槛很高的性质,推出来就满分,推不出来就\(0\)分,正推和反推的难度完全不是一个思维量级。题意Link给一个只有\(1\)和\(2\)......
  • E71 树形DP+二分 P3523 [POI2011] DYN-Dynamite
    视频链接:   P3523[POI2011]DYN-Dynamite-洛谷|计算机科学教育新生态//树形DP+二分O(nlogn)#include<iostream>#include<cstring>#include<algorithm>usingnamespacestd;intread(){intx=0,f=1;charc=getchar();while(c>'9'||c......
  • P1668 USACO04DEC Cleaning Shifts S 题解
    P1668USACO04DECCleaningShiftsS-洛谷分析这道题最快的做法应该是贪心,但是线段树优化DP也可以做。首先看到此题,可以想到一个很暴力的区间DP:\(f[i][j]\)表示在\([i,j]\)时段内最少需要的奶牛数量。对于每头牛的空闲时段\([l,r]\),其每个子区间答案均为\(1\);对于......
  • Redshift渲染的四个小技巧,实现照片级效果!
    在进行3D项目工作时,有时需要提供逼真的渲染结果。有许多渲染器可以简单地实现这一点,但像Redshift这样的渲染器并非如此。你需要关注更多的设置,本文将介绍如何在Redshift中获得逼真的渲染效果,以及如何通过云渲染技术进一步提高渲染效率和质量。一、Redshift中获得逼真渲染效果的......
  • [ABC150F] Xor Shift
    题意给定两个序列\(a,b\),求将\(b\)循环移位\(k\)位,再给所有\(b_i\oplusx\),求所有满足条件的\((k,x)\)。\(n\le2\times10^5\)。Sol对于区间异或,容易想到差分。考虑对\(a\)和\(b\)分别差分,注意到差分后\(x\)直接消失了!也就是:\(a_0\oplusa_1=b_{(......
  • 洛谷题单指南-分治与倍增-P3517 [POI2011] WYK-Plot
    原题链接:https://www.luogu.com.cn/problem/P3517题意解读:有n个连续的点p1,p2,...,pn,将这n个点分成不超过m堆,每堆点连续,每一堆都缩成一个点qi,要使得原来的点p1~ps距离qi的最大值最小(最相似),求这个相似度,并计算一共分成几堆,以及每堆缩到的点qi的坐标。解题思路:要使得若干点缩到一......
  • ShiftAddAug:基于乘法算子训练的最新无乘法网络方案 | CVPR'24
    不包含乘法的运算符,如移位和加法,因其与硬件的兼容性而日益受到重视。然而,采用这些运算符的神经网络(NNs)通常表现出比具有相同结构的传统NNs更低的准确性。ShiftAddAug利用成本较高的乘法来增强高效但功能较弱的无乘法运算符,从而在没有任何推理开销的情况下提高性能。将一个ShiftAd......