题目大意
询问从区间最小值到区间最大值之间的每个数是否至少出现一次。
解题分析
拆成两个问题:
1.区间最大(最小)值:ST表;
2.区间种类数: 莫队算法/可持久化线段树;
若区间最大值 - 区间最小值 + 1 == 区间种类数,YES,否则为NO。
1.莫队算法 - O(n*sqrt(n))
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN = 100010;
int n, m, k, block;
int a[MAXN], b[MAXN], cnt[MAXN], bel[MAXN], ans[MAXN];
int f[MAXN][20][2], cur;
struct Mo{
int l, r, id;
friend bool operator < (const Mo &a, const Mo &b){
if(bel[a.l] == bel[b.l]) return a.r < b.r;
return bel[a.l] < bel[b.l];
}
}q[MAXN];
inline int read()
{
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9'){x = x * 10 + (c - '0'); c = getchar();}
return x * f;
}
void st_prework()
{
for(int i = 1; i <= n; i ++) f[i][0][0] = f[i][0][1] = a[i];
int t = log(n) / log(2) + 1;
for(int j = 1; j < t; j ++){
for(int i = 1; i <= n - (1 << j) + 1; i ++){
f[i][j][0] = min(f[i][j - 1][0], f[i + (1 << (j - 1))][j - 1][0]);
f[i][j][1] = max(f[i][j - 1][1], f[i + (1 << (j - 1))][j - 1][1]);
}
}
}
int fmin(int l, int r)
{
int k = log(r - l + 1)/log(2);
return min(f[l][k][0], f[r - (1 << k) + 1][k][0]);
}
int fmax(int l, int r)
{
int k = log(r - l + 1)/log(2);
return max(f[l][k][1], f[r - (1 << k) + 1][k][1]);
}
void add(int x)
{
if(++ cnt[x] == 1) cur ++;
}
void del(int x)
{
if(-- cnt[x] == 0) cur --;
}
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int T = read();
while(T --){
n = read(); block = sqrt(n);
m = read();
for(int i = 1; i <= n; i ++){
a[i] = b[i] = read();
bel[i] = (i - 1)/block + 1;
}
st_prework();
sort(b + 1, b + n + 1);
k = unique(b + 1, b + n + 1) - b - 1;
for(int i = 1; i <= n; i ++) a[i] = lower_bound(b + 1, b + k + 1, a[i]) - b;
for(int i = 1; i <= m; i ++){
q[i].l = read(); q[i].r = read();
q[i].id = i;
}
sort(q + 1, q + m + 1);
int l = 1, r = 0;
cur = 0;
memset(cnt, 0, sizeof(cnt));
for(int i = 1; i <= m; i ++){
int mi = fmin(q[i].l, q[i].r);
int ma = fmax(q[i].l, q[i].r);
while(l < q[i].l) del(a[l ++]);
while(l > q[i].l) add(a[-- l]);
while(r < q[i].r) add(a[++ r]);
while(r > q[i].r) del(a[r --]);
ans[q[i].id] = ma - mi + 1 == cur ? 1 : 0;
}
for(int i = 1; i <= m; i ++)
puts(ans[i] ? "YES" : "NO");
}
return 0;
}
2.可持久化线段树 - O(n*log(n))
对每个位置建一棵线段树, rt[i] r t [ i ] 记录截止至第 i i 个位置,上不重复数字的个数。从左往右建树:若未出现过 a[i] a [ i ] ,直接将 a[i] a [ i ] 插入到 rt[i] r t [ i ] 的 i i 位置;若出现过,先删除 rt[i−1] r t [ i − 1 ] 中对应位置的 a[i] a [ i ] ,再把 a[i] a [ i ] 插入到 rt[i] r t [ i ] 的 i i 位置。求区间种类数时,只需询问树中 [l,r] [ l , r ] 区间之和即可。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
const int MAXN = 100010;
inline int read()
{
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9'){x = x * 10 + (c - '0'); c = getchar();}
return x * f;
}
int n, m, tmp;
int a[MAXN], f[MAXN][20][2];
map<int, int>pos;
void st_prework()
{
for(int i = 1; i <= n; i ++) f[i][0][0] = f[i][0][1] = a[i];
int t = log(n) / log(2) + 1;
for(int j = 1; j < t; j ++){
for(int i = 1; i <= n - (1 << j) + 1; i ++){
f[i][j][0] = min(f[i][j - 1][0], f[i + (1 << (j - 1))][j - 1][0]);
f[i][j][1] = max(f[i][j - 1][1], f[i + (1 << (j - 1))][j - 1][1]);
}
}
}
int fmin(int l, int r)
{
int k = log(r - l + 1)/log(2);
return min(f[l][k][0], f[r - (1 << k) + 1][k][0]);
}
int fmax(int l, int r)
{
int k = log(r - l + 1)/log(2);
return max(f[l][k][1], f[r - (1 << k) + 1][k][1]);
}
struct PresidentTree{int lc, rc, sum;}pt[MAXN * 22];
int num, rt[MAXN];
#define mid ((l + r) >> 1)
#define lc(x) pt[x].lc
#define rc(x) pt[x].rc
#define sum(x) pt[x].sum
void build(int &k1, int l, int r)
{
k1 = ++ num; sum(k1) = 0;
if(l == r) return;
build(lc(k1), l, mid);
build(rc(k1), mid + 1, r);
}
void modify(int &k1, int pre, int l, int r, int p, int v)
{
k1 = ++ num; pt[k1] = pt[pre]; sum(k1) = sum(pre) + v;
if(l == r) return;
if(p <= mid) modify(lc(k1), lc(pre), l, mid, p, v);
else modify(rc(k1), rc(pre), mid + 1, r, p, v);
}
int query(int k1, int l, int r, int ql, int qr)
{
if(ql <= l && r <= qr) return sum(k1);
int ans = 0;
if(ql <= mid) ans += query(lc(k1), l, mid, ql, qr);
if(qr > mid) ans += query(rc(k1), mid + 1, r, ql, qr);
return ans;
}
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int T = read();
while(T --){
n = read(), m = read();
for(int i = 1; i <= n; i ++) a[i] = read();
st_prework();
num = 0; pos.clear();
build(rt[0], 1, n);
for(int i = 1; i <= n; i ++){
if(pos[a[i]]){
modify(tmp, rt[i - 1], 1, n, pos[a[i]], -1);
modify(rt[i], tmp, 1, n, i, 1);
}
else{
modify(rt[i], rt[i - 1], 1, n, i, 1);
}
pos[a[i]] = i;
}
while(m --){
int l = read(), r = read();
int mi = fmin(l, r);
int ma = fmax(l, r);
tmp = query(rt[r], 1, n, l, r);
puts(tmp == ma - mi + 1 ? "YES" : "NO");
}
}
return 0;
}