POJ 3581 Sequence
题目大意:
给N个数(N不超过200000), 保证第一个数比后面的都大, 要求你把数列分成连续的三段, 分别逆序再拼起来, 要求新的串字典序最小.
简要分析:
一个直接的思路是想把最小的一个数作为第一段的末尾, 这样是不是最优的呢? 是的, 因为第一个数比后面的数都大, 于是你把所有的最小的数作为第一段的末尾逆序时, 最大的数之前的部分的字典序肯定不一样. 于是就先把整个序列逆序, 求一个后缀数组, 按i从小到大考虑sa[i], 因为第一个串的长度最多是N - 2, 所以sa[i]必须大于2, 找到最小的i即可.
于是问题转化为, 一个数字串, 分成两段并分别逆序, 使得字典序最小. 假设序列为s, 长度为m, 第一串的末尾为k, 则原来的串s1s2..sk..sk+1sk+2..sm, 在操作后变成了sksk-1..s1smsm-1..sk+1, 注意到这个串是smsm-1sm-2..s1smsm-1sm-2..s1的子串, 于是做法就明了了: 把s逆序并在后面复制一遍, 做一次后缀数组, 找到最小的i, 使得i不超过m, 且要求它大于1(要保证是两个串). 于是时间复杂度O(NlogN), 其实就是后缀数组的复杂度, 我写的倍增啦~
代码实现:
View Code
1 #include <cstdio>
2 #include <cstdlib>
3 #include <cstring>
4 #include <algorithm>
5 using namespace std;
6
7 const int MAX_N = 200000, MAX_R = MAX_N * 2;
8 int n, s[MAX_R + 1], sa[MAX_R + 1], rank[MAX_R + 1], height[MAX_R + 1], cnt[MAX_R + 1];
9 int t[MAX_R + 1], o[MAX_N + 1], m;
10
11 struct node_t {
12 int v[2], p;
13 bool operator == (const node_t &t) const {
14 return v[0] == t.v[0] && v[1] == t.v[1];
15 }
16 } nd[MAX_R + 1], tp[MAX_R + 1];
17
18 int find(int x) {
19 int l = 1, r = m + 1;
20 while (l + 1 < r) {
21 int mid = (l + r) >> 1;
22 if (o[mid] <= x) l = mid;
23 else r = mid;
24 }
25 return l;
26 }
27
28 void ra(int b, int l) {
29 for (int i = 1; i >= 0; i --) {
30 memset(cnt, 0, sizeof(int) * (b + 1));
31 for (int j = 1; j <= l; j ++) cnt[nd[j].v[i]] ++;
32 for (int j = 1; j <= b; j ++) cnt[j] += cnt[j - 1];
33 for (int j = l; j >= 1; j --) tp[cnt[nd[j].v[i]] --] = nd[j];
34 memcpy(nd, tp, sizeof(node_t) * (l + 1));
35 }
36 for (int i = 1, j = 1, k = 1; i <= l; i = j, k ++)
37 while (j <= l && nd[j] == nd[i]) rank[nd[j ++].p] = k;
38 }
39
40 int main() {
41 scanf("%d", &n);
42 for (int i = 1; i <= n; i ++) scanf("%d", &s[i]);
43 memcpy(o + 1, s + 1, sizeof(int) * n);
44 sort(o + 1, o + n + 1);
45 m = unique(o + 1, o + n + 1) - (o + 1);
46 for (int i = 1; i <= n; i ++) s[i] = find(s[i]);
47
48 memcpy(t + 1, s + 1, sizeof(int) * n);
49 reverse(t + 1, t + n + 1);
50 for (int i = 1; i <= n; i ++) nd[i].v[0] = t[i], nd[i].v[1] = 0, nd[i].p = i;
51 ra(m, n);
52 for (int st = 1; st < n; st <<= 1) {
53 for (int i = 1; i <= n; i ++) {
54 nd[i].v[0] = rank[i], nd[i].v[1] = i + st <= n ? rank[i + st] : 0;
55 nd[i].p = i;
56 }
57 ra(n, n);
58 }
59 for (int i = 1; i <= n; i ++) sa[rank[i]] = i;
60
61 int fir = 1;
62 while (sa[fir] <= 2) fir ++;
63 for (int i = sa[fir]; i <= n; i ++) printf("%d\n", o[t[i]]);
64 memset(t, 0, sizeof(t));
65 int new_n = sa[fir] - 1;
66 for (int i = 1; i <= new_n; i ++) t[i] = s[i + n - sa[fir] + 1];
67 reverse(t + 1, t + new_n + 1);
68 for (int i = 1; i <= new_n; i ++) t[i + new_n] = t[i];
69 new_n *= 2;
70 for (int i = 1; i <= new_n; i ++) nd[i].v[0] = t[i], nd[i].v[1] = 0, nd[i].p = i;
71 ra(m, new_n);
72 for (int st = 1; st < new_n; st <<= 1) {
73 for (int i = 1; i <= new_n; i ++) {
74 nd[i].v[0] = rank[i], nd[i].v[1] = i + st <= new_n ? rank[i + st] : 0;
75 nd[i].p = i;
76 }
77 ra(new_n, new_n);
78 }
79 for (int i = 1; i <= new_n; i ++) sa[rank[i]] = i;
80 fir = 1;
81 while (sa[fir] > new_n / 2 || sa[fir] == 1) fir ++;
82 for (int i = 0; i < new_n / 2; i ++) printf("%d\n", o[t[sa[fir] + i]]);
83
84 return 0;
85 }