只能想出个大概
没有加法是主席树
有了加法对应的就是主席树上一个区间而不是一个儿子了
查询的时候,并不是在主席树上查询,而只是一种线段树式的查询
如果+x后[l,mid]或[mid+1,r]有数,则往对应方向走
查询[l,mid]和[mid+1,r]就在主席树上查询
左儿子[l,mid]对应了区间[l-x,mid-x]
右儿子[mid+1,r]对应了区间[mid+1-x,r-x]
没有加法是主席树
有了加法对应的就是主席树上一个区间而不是一个儿子了
查询的时候,并不是在主席树上查询,而只是一种线段树式的查询
如果+x后[l,mid]或[mid+1,r]有数,则往对应方向走
查询[l,mid]和[mid+1,r]就在主席树上查询
左儿子[l,mid]对应了区间[l-x,mid-x]
右儿子[mid+1,r]对应了区间[mid+1-x,r-x]
一共会二分logn次,一次查询复杂度为O(logn),总复杂度O(mlog^2n)
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 200010
#define N 6000010
#define mx (1<<19)-1
using namespace std;
int root[maxn],lch[N],rch[N],size[N];
int n,T,tot;
int modify(int pre,int l,int r,int x)
{
int now=++tot;
if (l==r)
{
lch[now]=rch[now]=0;size[now]=size[pre]+1;
}
else
{
int mid=(l+r)/2;
if (x<=mid)
{
lch[now]=modify(lch[pre],l,mid,x);rch[now]=rch[pre];
}
else
{
rch[now]=modify(rch[pre],mid+1,r,x);lch[now]=lch[pre];
}
size[now]=size[lch[now]]+size[rch[now]];
}
return now;
}
int Query(int root1,int root2,int l,int r,int L,int R)
{
if (L<=l && r<=R) return size[root2]-size[root1];
int mid=(l+r)/2,ans=0;
if (L<=mid) ans+=Query(lch[root1],lch[root2],l,mid,L,R);
if (mid<R) ans+=Query(rch[root1],rch[root2],mid+1,r,L,R);
return ans;
}
int query(int i,int l,int r,int L,int R,int b,int x)
{
if (l==r) return l^b;
int mid=(l+r)/2,D=((b>>i)&1);
if (D==1)
{
if (mid-x<0) return query(i-1,mid+1,r,L,R,b,x);
if (Query(root[L-1],root[R],0,mx,max(l-x,0),max(mid-x,0))) return query(i-1,l,mid,L,R,b,x);
else return query(i-1,mid+1,r,L,R,b,x);
}
else
{
if (mid-x<0) return query(i-1,mid+1,r,L,R,b,x);
if (Query(root[L-1],root[R],0,mx,max(mid+1-x,0),max(r-x,0))) return query(i-1,mid+1,r,L,R,b,x);
else return query(i-1,l,mid,L,R,b,x);
}
}
int main()
{
scanf("%d%d",&n,&T);
root[0]=lch[0]=rch[0]=size[0]=0;
for (int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
root[i]=modify(root[i-1],0,mx,x);
}
while (T--)
{
int b,x,l,r;
scanf("%d%d%d%d",&b,&x,&l,&r);
printf("%d\n",query(18,0,mx,l,r,b,x));
}
return 0;
}