标签:
题意是给你n个数字组成的序列将序列切成三段, 然后将三段逆序之后字典序最小, 问你该怎么切,保证序列中第一个数最大, 我们可以找到两个切点然后翻转即可, 第一个切点可以将整个序列翻转之后找出最小的后缀即可, 对于第二个假设我们将ABCD序列中AB CD分别翻转得到BADC字典序最小, 那么我们可以构造一个序列DCBADCBA按照第一种处理即可得到第二个切点, 代码如下:
#include <algorithm> #include <cstring> #include <cstdio> using namespace std; const int maxn = 200000 + 100; int n, k; int a[maxn]; int rk[maxn], temp[maxn]; bool compare_sa(int i, int j){ if(rk[i] != rk[j]) return rk[i] < rk[j]; else { int ri = i+k<=n?rk[i+k]:-1; int rj = j+k<=n?rk[j+k]:-1; return ri < rj; } } void construct_sa(int a[], int n, int sa[]){ for(int i=0; i<=n; i++) { sa[i] = i; rk[i] = i<n?a[i]:-1; } for(k=1; k<=n; k*=2){ sort(sa, sa+n+1, compare_sa); temp[sa[0]] = 0; for(int i=1; i<=n; i++){ temp[sa[i]] = temp[sa[i-1]] + (compare_sa(sa[i-1], sa[i])?1:0); } for(int i=0; i<=n; i++) rk[i] = temp[i]; } } int rev_a[maxn], sa[maxn]; int main() { scanf("%d", &n); for(int i=0; i<n; i++) scanf("%d", &a[i]); reverse_copy(a, a+n, rev_a); construct_sa(rev_a, n, sa); int p1; for(int i=0; i<=n; i++){ p1 = n - sa[i]; if(p1>=1 && n-p1>=2) break; } int m = n-p1; reverse_copy(a+p1, a+n, rev_a); reverse_copy(a+p1, a+n, rev_a+m); construct_sa(rev_a, 2*m, sa); int p2; for(int i=0; i<=2*m; i++){ p2 = p1 + m - sa[i]; if(p2-p1>=1 && n-p2>=1) break; } reverse(a, a+p1); reverse(a+p1, a+p2); reverse(a+p2, a+n); for(int i=0; i<n; i++) printf("%d\n", a[i]); return 0; }
标签:
原文地址:http://www.cnblogs.com/xingxing1024/p/5327328.html