【题意】给出一个长度为n的数组(n<=100W).每个数都<=100W。然后m次询问,每次询问l..r区间内是否为1..r-l+1的一个排列
【解题方法】一个区间是排列只需要区间和为2len(len+1)(len为区间长度),且互不相同,对于第一个问题我们用前缀和解决,对于第二个问题,预处理每个数的上次出现位置,记它为pre,互不相同即区间中pre的最大值小于左端点,使用线段树或Sparse Table即可在O(n)/O(nlogn)的预处理后 O(logn)/O(1)回答每个询问.不过我们还有更简单的hash做法,对于[1..n]中的每一个数随机一个64位无符号整型作为它的hash值,一个集合的hash值为元素的异或和,预处理[1..n]的排列的hash和原序列的前缀hash异或和,就可以做到线性预处理,O(1)回答询问.
【AC 代码】
<span style="font-family:Arial;">//Hash 算法</span>
#include <time.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1000010;
#define uLL unsigned __int64
uLL XOR[maxn],a[maxn],sum[maxn];
inline uLL RANDuLL()
{
uLL one=1;
uLL RAND=(rand()+rand())*(one<<47)+(rand()+rand())*(one<<31)+(rand()+rand())*(one<<15)+(rand()+rand());
return RAND;
}
int main()
{
srand(time(NULL));
XOR[0]=0;
for(int i=1; i<maxn; i++){
a[i]=RANDuLL();
XOR[i]=XOR[i-1]^a[i];
}
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
sum[0]=0;
int x;
for(int i=1; i<=n; i++){
scanf("%d",&x);
sum[i]=a[x]^sum[i-1];
}
int l,r;
while(m--){
scanf("%d%d",&l,&r);
if(XOR[r-l+1]==(sum[r]^sum[l-1])){
puts("YES");
}else{
puts("NO");
}
}
}
}
【线段树版本】
<span style="font-family:Arial;">//代码来自上决</span>
<pre name="code" class="cpp">#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
using namespace std;
typedef __int64 LL;
#define NN 1000010
struct node
{
int l,r;
int id;
}q[NN];
bool cmp(node t1,node t2)
{
return t1.r<t2.r;
}
int mx[NN*3];
LL sum[NN*3];
void push_up(int id)
{
sum[id]=sum[id<<1]+sum[id<<1|1];
mx[id]=max(mx[id<<1],mx[id<<1|1]);
}
int MAX;
LL SUM;
void build()
{
memset(mx,0,sizeof(mx));
memset(sum,0,sizeof(sum));
}
void update(int id,int L,int R,int pos,int val,int mxx)
{
if(L==R)
{
sum[id]=(LL)val;
mx[id]=mxx;
return ;
}
int mid=(L+R)>>1;;
if(pos<=mid)
update(id<<1,L,mid,pos,val,mxx);
else
update(id<<1|1,mid+1,R,pos,val,mxx);
push_up(id);
}
void query(int id,int L,int R,int l,int r)
{
if(l<=L&&R<=r)
{
MAX=max(MAX,mx[id]);
SUM+=sum[id];
return ;
}
int mid=(L+R)>>1;
if(l<=mid)
query(id<<1,L,mid,l,r);
if(mid<r)
query(id<<1|1,mid+1,R,l,r);
}
int a[NN],p[NN],pre[NN];
inline int getint()
{
int c;
while (c = getchar(), c<'0' || '9'<c);
int res = c - 48;
while (c = getchar(), '0' <= c&&c <= '9') res = (res << 3) + res + res + c - 48;
return res;
}
int main()
{
int n,m;
while(scanf("%d %d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++) a[i]=getint();
for(int i=1;i<=m;i++)
{
q[i].l=getint();
q[i].r=getint();
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
build();
memset(p,0,sizeof(p));
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)
{
pre[i]=p[a[i]];
p[a[i]]=i;
}
int t=1;
memset(p,0,sizeof(p));
for(int i=1;i<=n;i++)
{
update(1,1,n,i,a[i],pre[i]);
while(i==q[t].r)
{
SUM=0;
MAX=0;
LL tt=(LL)(q[t].r-q[t].l+1);
//printf("%d %d %d??\n",q[t].l,q[t].r,q[t].id);
LL sum=tt*(tt+1)/2;
query(1,1,n,q[t].l,q[t].r);
//printf("%I64d %d %I64d %d\n",tmp.sum,tmp.mx ,sum,q[t].l);
if(sum==SUM && MAX < q[t].l)
{
p[q[t].id]=1;
}
++t;
}
}
for(int i=1;i<=m;i++)
{
if(p[i]==1)
puts("YES");
else puts("NO");
}
}
return 0;
}