题意:说有个序列共n(n<=10^6)个数(每个数都不大于n)。有m(m<=10^6)次询问,每次给你[l,r],问你在区间[l,r]内的r-l+1个数是否是[1,2,3,4,5,6,........r-l+1]的全排列?是输出“YES”,否则输出“NO”。
题解:此题不需要对序列进行修改所以,我们用sum[r] - sum[l-1]来获得区间[l,r]的和。线段树获得区间内相同元素的最大地址,具体看代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 1000009
typedef long long LL;
int num[N],nod[N*4],tem[N],pre[N];
LL sum[N];
int max(int a,int b){ return a>b?a:b; }
void build_tree(int l,int r,int k){
if(l == r){
nod[k] = pre[r];
return;
}
int mid = (l + r) >> 1;
build_tree(l,mid,k<<1);
build_tree(mid+1,r,k<<1|1);
nod[k] = max(nod[k<<1],nod[k<<1|1]);
}
int quary(int L,int R,int l,int r,int k) {
if(L<=l&&R>=r) return nod[k];
int mid = (l + r) >> 1;
int res = 0;
if(L<=mid) res = max(res,quary(L,R,l,mid,k<<1));
if(R>mid) res = max(res,quary(L,R,mid+1,r,k<<1|1));
return res;
}
void getPre(int n){
memset(tem,0,sizeof(tem));
for(int i = 1;i<=n;i++){
pre[i] = tem[num[i]];
tem[num[i]] = i;
}
}
int main() {
int n,m;
LL l,r;
//freopen("Test.txt","r",stdin);
while(~scanf("%d%d",&n,&m)){
sum[0] = 0;
for(int i = 1;i<=n;i++){
scanf("%d",&num[i]);
sum[i] = sum[i-1] + (LL)num[i];
}
getPre(n);
build_tree(1,n,1);
for(int i = 1;i<=m;i++){
scanf("%I64d%I64d",&l,&r);
LL t = (r-l+2)*(r-l+1)/2,u = sum[r] - sum[l-1];
if(u!=t){ printf("NO\n"); continue; }
int res = quary(l,r,1,n,1);
printf("%s\n",res>=l?"NO":"YES");
}
}
return 0;
}