洛谷P3516 PRZ-Shift [POI2011] 构造

正解:构造

解题报告:

传送门!

umm这题就是很思维的?就是想到了就A了想不到就做不出来,然而我也只能是做到理解不知道怎么想出来,,,感觉构造题什么的就很真诚,一点套路也没有,所以像我这种没有脑子只会投机取巧找规律的弱鸡就完全想不到QAQ撑死也不够是能理解解法而已QAQ希望多做一点这类的题目能有帮助趴QAQ

首先嗦嗦这题最基本的变化,就是,假如我现在已经确定了一段[1,i]了,现在要把第i+1放到第i个后面去,假如是酱婶的:balabala 1 2 ... i balabala i+1 balabala(事实上最前面那个balabala是不可能存在的存在也很容易消掉的,但是不要在意这种细节反正没有影响×只是代码有点儿区别注意下就是了qwq

显然的是当[1,i]后面乜有数了并且第i+1个在第一个的时候就能用i个操作a于是就能得到[1,i+1]balabala了,然后一直这么做做做做下去就欧克了

那怎么实现呢,首先用一堆a把第i+1个放到第一个,这儿不难?然后考虑怎么做到把[1,i]之后那些杂七杂八的数都移到前面去并且保持i+1在第一个?其实比较容易想到?就用俩A然后i+1就到了第三位这时候再用个B,i+1就又到第一个了

然后注意一下的是如果到最后i+1在第一个,就直接做;如果在第三个,用一次B然后直接做;如果在第二个呢?当然也不难咯,用两次B就可以了

另外一个需要注意的地方是当解决得差不多了只剩最后两个的时候,如果出现了n,n-1,1,2,...,n-2的情况,按照我们之前的思路,就是用两次B然后就能让n-1到第一位然后就这么顺利地做下去辽

但是!显然这个情况下就不欧克了鸭QAQ因为n被夹到[1,n-2]这个区间里去了就废了

所以考虑换一种变化方式

就是,直接假装n-1在第一个时候的样子移动[1,n-2],不过是当第n个在第三个的时候使用B

这样最后的效果应该就是变成酱婶的:n,1,2,...,n-1

大概能get到?

然后再走n-1次A就欧克了!

然后如果在刚刚说的情况中,n是奇数,辣就是说n-2是奇数,就无法每次都移动俩,所以就无解输出NIE

over!

umm然后放个代码QwQ

#include<bits/stdc++.h>
using namespace std;
#define rp(i,x,y) for(register int i=x;i<=y;++i)

const int N=5000;
int n,a[N],b[N],op[N*N],len[N*N],ans,flag;

inline int read()
{
    char ch=getchar();int x=0;bool y=1;
    while(ch!='-' && (ch>'9' || ch<'0'))ch=getchar();
    if(ch=='-')ch=getchar(),y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
    return y?x:-x;
}
void mva(int l){rp(i,1,n)b[(i+l)%n]=a[i];b[n]=b[0];rp(i,1,n)a[i]=b[i];}
void mvb(int &x,int &y,int &z){int tmp=z;z=y,y=x,x=tmp;}
void fd(int i,int &x){rp(j,1,n)if(a[j]==i){x=j;return;}}
void final(int x)
{
    int w=n+1,mva=(n-x)&1;
    for(int i=x+mva;a[i+1]!=1;i--)b[--w]=a[i];w=1;rp(i,x+1+mva,n)b[++w]=a[i];
    for(int i=2;a[i]!=1;i++)b[++w]=a[i];rp(i,1,n)a[i]=b[i];
    if(!mva)return;
    rp(i,3,n-1)b[i+1]=a[i];
    b[1]=a[1];b[2]=a[2],b[3]=a[n];
    rp(i,1,n)a[i]=b[i];
}
void ad(int l,int k)
{
    if(op[ans]==k)len[ans]+=l;else len[++ans]=l,op[ans]=k;
    if(op[ans]==1)len[ans]%=n;else len[ans]%=3;
    if(!len[ans])--ans;
}
void printans()
{
    if(flag){printf("NIE\n");return;}
    printf("%d\n",ans);
    rp(i,1,ans)
    {
        printf("%d%c",len[i],op[i]+'a'-1);
        if(i!=ans)printf(" ");
    }
}
void dolast()
{
    int x;fd(1,x);int tt=(n-x+1)%n;
    if(tt)mva(tt),ad(tt,1);
    if(a[n-1]==n-1)return;
    if(n&1){flag=1;return;}
    mva(2),ad(2,1);
    while(a[n]!=n-1){ad(2,1);mva(2);ad(1,2);mvb(a[1],a[2],a[3]);}
    ad(n-1,1);mva(n-1);
}
int main()
{
    n=read();ans=0;flag=0;
    rp(i,1,n)a[i]=read();
    rp(i,2,n-2)
    {
        int x;fd(i,x);if(x==i)continue;int tt=n-x+1;
        mva(tt);ad(tt,1);fd(i-1,x);
        rp(k,1,(n-x)>>1)ad(2,1),ad(1,2);
        if((n-x)&1)ad(1,1),ad(2,2);
        ad(i-1,1);final(x);mva(i-1);
    }
    dolast();printans();
}
好难打啊,,,死亡QAQ

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值