LOJ #10121. 「一本通 4.2 例 3」与众不同
summarization
给出一个长度为 \(n\) 数列 \(a\) 和若干个询问,询问某一段区间内最长的「完美序列」的长度。(「完美序列」:一段连续的序列满足序列中的数互不相同)
solution
显然在一个数列的所有「完美序列」中,我们肯定可以找到若干个最长「完美序列」,使其余的「完美序列」都被这些最长的「完美序列」中的一个包含。所以考虑求出以第 \(i\) 个数结尾的最长的「完美序列」(\(1\le i\le n\)),然后根据询问查找(后文会具体讲)。
那么设 \(st_i\) 为以第 \(i\) 个数结尾的最长的「完美序列」的起始位置; \(len_i\) 为以第 \(i\) 个数结尾的最长的「完美序列」的长度,显然 \(len_i = i - st_i + 1\);\(lst_x\) 为 \(x\) 最近出现的位置。
\(lst_x\) 很好维护,只要在遍历到第 \(i\) 个时,使 \(lst_{a_i}=i\) 即可。\(st_i\) 可以用递推的方法维护:\(st_i=\max(st_{i-1},lst_{a_i}+1)\)。
然后处理询问,设询问区间为 \([l,r]\):
由上式,\(st_i\) 单调不减。于是我们一定可以找到一个 \(i\),使 \(st_i\ge l,st_{i-1}<l\),设此时的 \(i\) 为 \(mid\)。
那么 \([l,r]\) 就可以分成两部分:\([l,mid-1]\) 和 \([mid,r]\)。
- \([l,mid-1]\):答案显然为 \(mid - l\)。
- \([mid,r]\):答案为 \(\max\limits_{i=mid}^rlen_i\)(是一个 RMQ 问题,可以 ST 表或者别的)
取个最大值。
PS:注意细节,如 \(mid\) 的特殊位置。
code
CI N = 2e5, LGN = 20, E = 1e6; int n, m, a[N + 5], st[N + 5], len[N + 5], lst[N + 5], lg[N + 5], fa[N + 5][LGN + 5];
int ask (int l, int r) {
int L = l, R = r, ans = r + 1, now = 0; W (L <= R) {int mid = (L + R) >> 1; if (st[mid] >= l) ans = mid, R = mid - 1; else L = mid + 1;}
if (ans > l) now = ans - l; if (ans <= r) {int k = lg[r - ans + 1]; now = max (now, max (fa[ans][k], fa[r - (1 << k) + 1][k]));} return now;
}
int main () {
RI i, j; for (Read (n, m), i = 1; i <= n; ++ i) Read (a[i]); for (i = 2; i <= n; ++ i) lg[i] = lg[i >> 1] + 1;
for (i = 1; i <= n; ++ i) st[i] = max (st[i - 1], lst[E + a[i]] + 1), len[i] = i - st[i] + 1, lst[E + a[i]] = i, fa[i][0] = len[i];
for (j = 1; j <= lg[n]; ++ j) for (i = 1; i <= n - (1 << j) + 1; ++ i) fa[i][j] = max (fa[i][j - 1], fa[i + (1 << j - 1)][j - 1]);
for (i = 1; i <= m; ++ i) {int x, y; Read (x, y); printf ("%d\n", ask(x + 1, y + 1));}
return 0;
}