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了。
这就是整个面向数据设计的算法了。
撒花~~
- bzoj2214
- BZOJ2214 [Poi2011]Shift
- BZOJ2214: [Poi2011]Shift
- 树莓派调大linux系统音量(命令行)
- Android API Guides——简介(一)
- 浏览器安全 -原理 时序 Render DOM树构建
- shell中执行一个字符串命令的方法
- 批处理命令学习笔记——删除系统默认共享
- bzoj2214
- 使用Fregata实现spark的逻辑回归算法
- 批处理命令学习笔记——系统加固
- HTML 代码规范
- static变量、static函数与普通变量、普通函数的区别
- POJ1664 记忆递归,简单易懂
- MySql存储过程及MySql常用流程控制语法
- Android API Guides——简介(二)
- 背包九讲里的01背包