My Luogu

「一本通 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;
}
posted @ 2022-09-17 18:10  lxy07830  阅读(166)  评论(0)    收藏  举报