「一本通 4.2 例 3」与众不同
可以预处理出每个右端点 \(r\) 对应的最长互不相同区间的左端点 \(st_r\)。
假设我们已经处理出了 \(st_{i - 1}\),那么如果 \([st_{i - 1}, i - 1]\) 中没有数与 \(a_i\) 相同,则 \([st_{i - 1}, i]\) 是以 \(i\) 结尾最长互不相同区间,反之以 \(i\) 结尾最长互不相同区间为 \([last_{a_i} + 1, i]\),其中 \(last_{x}\) 是 \(x\) 上一次出现的位置,整理得
\[st_i = \max\left\{last_{a_i} + 1, st_{i - 1}\right\}
\]
[P3901 数列找不同]
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[100005], st[100005], len[100005], last[100005];
int main(){
scanf("%d%d" ,&n ,&m);
for(int i = 1; i <= n; ++i){
scanf("%d" ,&a[i]);
}
for(int i = 1; i <= n; ++i){
st[i] = max(last[a[i]] + 1, st[i - 1]);
last[a[i]] = i;
}
for(int i = 1; i <= m; ++i){
int l, r;
scanf("%d%d" ,&l ,&r);
if(st[r] <= l){
printf("Yes\n");
}else{
printf("No\n");
}
}
return 0;
}
可以发现 \(st\) 具有单调性。
对于每次询问的区间 \([l, r]\),我们不知道所有以 \(i \in [l, r]\) 为右端点的区间是否被 \(l\) 所截,因此需要求出一个值 \(mid\),使得右端点 \(\in [l, mid - 1]\) 的区间被 \(l\) 所截(此时最长长度为 \(mid - l\) ),右端点 \(\in [mid, r]\) 的区间不会被 \(l\) 所截(答案为 \(\max\{i - st_i + 1\}\))。
由于 \(st\) 具有单调性,因此可以使用二分查找 \(mid\) 。对于 \(i \in [mid, r]\) 的情况可以用ST表维护。
题目中只说 \(|a_i| \le 10^6\),这意味着 \(a_i\) 可能是负数,此时需要考虑将 \(a_i\) 加上 \(10^6\) 或直接离散化。
注意题目中的下标是从 \(0\) 开始的
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[200005], st[200005], len[200005], last[200005];
int f[200005][20];
vector<int> rk;
int query(int l, int r){
int k = log2(r - l + 1);
return max(f[l][k], f[r - (1 << k) + 1][k]);
}
int main(){
scanf("%d%d" ,&n ,&m);
for(int i = 1; i <= n; ++i){
scanf("%d" ,&a[i]);
rk.push_back(a[i]);
}
sort(rk.begin(), rk.end());
rk.erase(unique(rk.begin(), rk.end()), rk.end());
for(int i = 1; i <= n; ++i){
a[i] = lower_bound(rk.begin(), rk.end(), a[i]) - rk.begin() + 1;
}
for(int i = 1; i <= n; ++i){
st[i] = max(last[a[i]] + 1, st[i - 1]);
len[i] = i - st[i] + 1;
f[i][0] = len[i];
last[a[i]] = i;
}
for(int j = 1; (1 << j) <= n; ++j){
for(int i = 1; i <= n; ++i){
f[i][j] = f[i][j - 1];
if(i + (1 << (j - 1)) <= n){
f[i][j] = max(f[i][j], f[i + (1 << (j - 1))][j - 1]);
}
}
}
for(int i = 1; i <= m; ++i){
int l, r;
scanf("%d%d" ,&l ,&r);
l++;
r++;
int mid = lower_bound(st + l, st + r + 1, l) - st;
if(lower_bound(st + l, st + r + 1, l) == st + r + 1){//此时所有区间被 l 所截
printf("%d\n" ,r - l + 1);
}else{
printf("%d\n" ,max(mid - l, query(mid, r)));
}
}
return 0;
}
浙公网安备 33010602011771号