标签:写作 传送门 def front 没有 有一个 ret stl ack
题意:给出一个长为$N$的排列,有两种操作:$A$:将最后一个数字放到第一个;$B$:将第三个数字放到第一个。一次性使用某种操作$k$次写作$kA$或$kB$,其中在$kA$中$k < N$,在$kB$中$k < 3$。请给出一种方案使得序列变为$1,2,3...N$。$N \leq 2000$
一道比较难的构造题
我们考虑增量构造:假如我们已经将$1-i$排好了,如何将$i+1$排到它们后面。我们可以进行如下操作:
$1.$通过若干$A$操作将$i+1$放到序列开头的位置
$2.$重复$2A,1B$操作将$i$与$i+1$之间不必要的数字两两踢到$i+1$后面
$3.$如果序列最后还有一个多余的数字,使用$1A,2B$操作将它踢到$i+1$后面
这样$i$与$i+1$就能相连了。
但是当我们需要将$n-1$接上时,会有一些问题:
我们考虑这样的一个序列:$$n-1,1,2,3...n-2,n$$
如果我们使用$1A,2B$操作,$n$就会夹在$1$和$2$中间,打乱了我们之前排好的顺序。所以当我们需要排$n-1$和$n$时,这种方法是不可行的。不妨考虑另外一种构造方法。
我们将$n$移到第一个,也就是$$n,n-1,1,2,3...n-2$$
然后我们使用一次$2A,1B$操作,然后序列就变成了……
$$n,n-3,n-2,n-1,1,2,3...n-4$$
发现$n$的位置没有变,但是后面$n-1$个数在循环。那么我们不断重复该操作,直到序列变成$$n,1,2,3...n-2,n-1$$就行了。但实际上在当前情况下当$n$为奇数的时候是不可行的,因为在移动若干次之后,序列会变成$$n,2,3,4...n-1,1$$,再一次重复该操作又会把$n-1$踢到前面去了。
可以发现所有操作都是$O(n)$级别的,总操作次数是$O(n^2)$级别的,与题设刚好一致
稍微注意一下输出
关于序列移动的模拟操作建议使用链表。如果比较懒,可以使用STL中的deque,开O2的情况下效率还是比较优秀的(不开O2是最慢的)
下面的代码:O2 497ms,无O2 2146ms
1 #include<bits/stdc++.h> 2 #define MAXN 5000010 3 using namespace std; 4 5 inline int read(){ 6 int a = 0; 7 char c = getchar(); 8 while(!isdigit(c)) 9 c = getchar(); 10 while(isdigit(c)){ 11 a = (a << 3) + (a << 1) + (c ^ ‘0‘); 12 c = getchar(); 13 } 14 return a; 15 } 16 17 char done[MAXN] , allDone[MAXN]; 18 int step[MAXN] , allStep[MAXN] , pot[MAXN] , N , cnt; 19 deque < int > now; 20 21 inline void moveA(int s){ 22 if(done[cnt] != ‘a‘) 23 done[++cnt] = ‘a‘; 24 step[cnt] += s; 25 for(int i = 1 ; i <= s ; i++){ 26 now.push_front(now.back()); 27 now.pop_back(); 28 } 29 } 30 31 inline void moveB(int s){ 32 if(done[cnt] != ‘b‘) 33 done[++cnt] = ‘b‘; 34 step[cnt] += s; 35 for(int i = 1 ; i <= s ; i++){ 36 int p = now[2]; 37 now[2] = now[1]; 38 now[1] = now[0]; 39 now[0] = p; 40 } 41 } 42 43 inline void output(){ 44 int calc = 0; 45 for(int i = 1 ; i <= cnt ; i++) 46 if(done[i] != allDone[i]){ 47 if(step[i] %= (done[i] == ‘a‘ ? N : 3)){ 48 allDone[++calc] = done[i]; 49 allStep[calc] = step[i]; 50 } 51 } 52 else 53 if((allStep[i] + step[i]) % (done[i] == ‘a‘ ? N : 3)) 54 allStep[i] = (allStep[i] + step[i]) % (done[i] == ‘a‘ ? N : 3); 55 else 56 allDone[calc--] = 0; 57 cout << calc << endl; 58 for(int i = 1 ; i <= calc ; i++) 59 cout << allStep[i] << allDone[i] << ‘ ‘; 60 } 61 62 int main(){ 63 N = read(); 64 for(int i = 1 ; i <= N ; i++) 65 now.push_back(read()); 66 if(N == 1){ 67 putchar(‘0‘); 68 return 0; 69 } 70 else 71 if(N == 2){ 72 cout << (now[0] == 1 ? "0" : "1\n1a"); 73 return 0; 74 } 75 for(int i = 2 ; i < N - 1 ; i++){ 76 for(int j = 0 ; j < N ; j++) 77 if(now[j] == i){ 78 if(j) 79 moveA(N - j); 80 break; 81 } 82 while(now[N - 1] != i - 1) 83 if(now[N - 2] != i - 1){ 84 moveA(2); 85 moveB(1); 86 } 87 else{ 88 moveA(1); 89 moveB(2); 90 } 91 } 92 for(int i = 0 ; i < N ; i++) 93 if(now[i] == N){ 94 if(i) 95 moveA(N - i); 96 break; 97 } 98 if(now[1] == N - 1){ 99 if(N & 1){ 100 puts("NIE"); 101 return 0; 102 } 103 while(now[N - 1] != N - 1){ 104 moveA(2); 105 moveB(1); 106 } 107 } 108 moveA(N - 1); 109 output(); 110 return 0; 111 }
标签:写作 传送门 def front 没有 有一个 ret stl ack
原文地址:https://www.cnblogs.com/Itst/p/9824717.html