题目链接:点击打开链接
题目大意就是,给一段序列,长度为10^5,然后有10^5个询问,每次询问为一个区间,l为左边界,r为右边界,该区间是否为一个1到r-l+1的一个排列,即该区间是否满足包含1到r-l+1的所有元素。
分析一下,如果一个长度为n的区间包含从1到n的所有元素,那么在该区间之内,1到n的元素每个只出现一次,而且这个区间和应该为n(n+1)/2。第二个条件只需要处理前缀和即可,第一个条件则需要查重。
第一个我用的是线段树,每一个位置记录一下当前元素出现的上一个位置在哪里,然后以记录为元素建线段树,线段树的每个节点保存当前区间里重复出现的最后一个位置,对于查询只需要看重复位置是否出现在区间之内。
貌似出题人还提出一种哈希查重的方法,给1到n个元素随机一个哈希值,用集合元素的异或和作为哈希值,预处理出前n个元素的哈希值,然后o(1)查重...个人感觉存在满足哈希值相同且和相同的两个集合...用以自身为哈希值的方法交了一发,在一个大数据前面wa了,应该是个不稳定的方法...
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define N 1000010
#define ls rt << 1
#define rs rt << 1 | 1
#define LL long long
struct node{
int l,r;
int pre;
}T[N<<2];
LL sum[N];
int pre[N],now[N];
void Pushup(int rt){
T[rt].pre = max(T[ls].pre , T[rs].pre);
}
void build(int l,int r,int rt){
T[rt].l = l;
T[rt].r = r;
if(l == r){
T[rt].pre = now[l];
return ;
}
int m = (l + r) >> 1;
build(l,m,ls);
build(m + 1,r,rs);
Pushup(rt);
}
LL SUM(int l,int r){
LL a = r - l + 1,ret;
ret = ((LL)1+a)*a/(LL)2;
return ret;
}
int query(int l,int r,int rt){
if(l <= T[rt].l && T[rt].r <= r){
return T[rt].pre;
}
int ret = -1;
if( l <= T[ls].r ) ret = query(l,r,ls);
if( r >= T[rs].l) ret = max(ret, query(l,r,rs));
return ret;
}
int main()
{
int n,m,t,l,r;
LL ans;
sum[0] = 0;
while(~scanf("%d%d",&n,&m)){
memset(pre,-1,sizeof(pre));
for(int i=1;i<=n;i++){
scanf("%d",&t);
if(i==1){
sum[i] = t;
}
else sum[i] = sum[i - 1] + (LL)t;
// printf("%I64d ",sum[i]);//shan
if(pre[t] == -1){
now[i] = -1;
pre[t] = i;
}
else{
now[i] = pre[t];
pre[t] = i;
}
}
//printf("\n");// shan
build(1,n,1);
//printf("1\n");
for(int i=0;i<m;i++){
scanf("%d%d",&l,&r);
ans = sum[r] - sum[l - 1];
if(ans == SUM(l,r)){
ans = query(l,r,1);
if(ans >= l){
printf("NO\n");
}
else {
printf("YES\n");
}
}
else{
printf("NO\n");
}
}
}
return 0;
}