bzoj2214

来源:互联网 发布:超过usb3.0端口数限制 编辑:程序博客网 时间:2024/05/07 08:33

poi的好(毒)题。。
题意:
有一个1..n的排列,有两种操作:
(a) 将最后一个数移到最前面
(b) 把第三个数移到最前面
我们将连续进行k次同一个操作称为“一块操作”,表示为ka或kb。
找到一个操作序列使得进行这些操作后,排列变为1,2,3,…,n。
n<=2000

#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>#include<iostream>#define N 2100using namespace std;struct node{int o,d;}ans[N*N];int n,a[N],cnt,las,b[N];bool ok(){    for(int i=1;i<=n;i++) if(a[i]!=i) return 0;    return 1;}void print(){    printf("%d\n",cnt);    for(int i=1;i<=cnt;i++)     {        printf("%d",ans[i].d);        if(ans[i].o==1) printf("a ");        else printf("b ");    }}void ins(int o,int d){    if(o!=ans[cnt].o) {ans[++cnt].o=o;ans[cnt].d=d;}    else    {        ans[cnt].d+=d;ans[cnt].d%=n;        if(ans[cnt].d==0) cnt--;    }}bool solve(){    if(n%2) return 0;    ins(1,n-2);ins(2,2);    for(int i=n;i>4;i-=2) ins(1,n-2),ins(2,2);    if(n-4) ins(1,n-4);    return 1;}void make(){    while(a[1]!=1)    {        ins(1,1);        int t=a[1];a[1]=a[3];a[3]=a[2];a[2]=t;    }    if(a[2]==2) print();    else printf("NIE DA SIE\n");}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    if(ok()) {printf("0\n");return 0;}    if(n==2) {printf("1\n1a\n");return 0;}    if(n==3) {make();return 0;}    for(int k=2;k<=n-2;k++)    {        for(int i=2;i<=n;i++)            if(a[i]==k)            {                ins(1,n-i+1);                int bl=0;                for(int j=i;j<=n;j++) b[++bl]=a[j];                for(int j=1;j<i;j++) b[++bl]=a[j];                for(int j=1;j<=n;j++) a[j]=b[j];                break;            }        int las=0,pre;        for(int i=1;i<=n;i++) if(a[i]==k-1) {las=i;break;}        pre=las;        bool bo=0;        while(las<n)        {            if(las+2<=n) {ins(1,2);ins(2,1);las+=2;}            else {ins(1,1);ins(2,2);las++;bo=1;}        }        int bl=1;        b[1]=k;        for(int i=pre+1;i<=n;i++) b[++bl]=a[i];        for(int i=2;i<=pre;i++) b[++bl]=a[i];        for(int i=1;i<=n;i++) a[i]=b[i];        if(bo) swap(a[2],a[3]);        int oo=1;    }    if(a[2]!=n-1)     {        if(solve()==0) printf("NIE DA SIE\n");        else print();    }    else    {        ins(1,n-3);        print();    }    return 0;}

题解:
我的第一感觉,b操作的意义是能把某些数留在前三个位置
开始我想把1留在前三个位置,然后把2转到他旁边。再把1,2留在前三个位置,然后把3转到他们旁边。再。。似乎长度大于等于3就留不住了。。
那1..k留在前三个,把k+1转过去不行,就试试把k+1留在前三个,把1..k转过去吧。。
也就是说,核心在于我们已经在某处构造出了1..k,现在想把k+1加入,就用b操作把k+1留在前三个,然后1..k转一圈接上去。这是容易构造的。
注意这种构造方法是要用前三个位置做缓冲的,所以不破坏已经构造出的序列我们只能造出n-2的状态
此时序列要么是
n-2,n-1,n,1,…,n-3(这已经合法了)
要么是
n-2,n,n-1,1,…,n-3
一开始,我觉得此时必定不合法,因为再用b操作会破坏1..n-2的顺序。
于是自信一WA
下载数据后,发现这样一种情况
6 8 7 1 2 3 4 5是有解的!
看6a 2b 6a 2b 6a 2b后会发生什么
5 6 7 8 1 2 3 4
我们观察out的构造方法(捂脸)
把n-1放在第三个位置,然后(n-2)a让n-1去到第一个位置,再用2b把他放到第三个位置。相比操作前,n-1位置不变,而不看第三个位置所有位置右移n-2位,即左移2位。若干次的目的就是通过不断左移两位把原来在第二位的n移到第四位。
显然第二位可以通过若干次左移两位到达第四位的条件是n%2=0,如果这种方法也不行就是NIE DA SIE了。
这就是整个面向数据设计的算法了。
撒花~~

0 0
原创粉丝点击