区间异或和=贪心+可持久化字典树?

  先不管那个+d,我们来考虑正常的可持久化字典树.总的思想就是贪心:用一个主席树模仿字典树,我发现当线段树总区间是2^n-1时每个小区间内部的01分布就与深度有关.比如左半边这一位都是0右边都是1这个样子.这样就可以O(1)地判断某个区间内有没有数,因为是一整个的区间,又因为向下到达根节点需要logn,总的复杂度为m*logn.

  

int i,k,tx,ty;
int n,m,a[10000],o[10000],c[1000],sum;
int tot,rt[1000],lc[1000],rc[1000];
void build(int &now,int l,int r){
    tot++;
    now=tot;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    build(lc[now],l,mid);
    build(rc[now],mid+1,r);
    return ;
}
int built(int now,int l,int r){
    tot++;
    int t=tot;
    lc[t]=lc[now];rc[t]=rc[now];c[t]=c[now]+1;
    if(l==r)
        return t;
    int mid=(l+r)>>1;
    if(k<=mid)lc[t]=built(lc[t],l,mid);
    else      rc[t]=built(rc[t],mid+1,r);
    return t;
}    
int ask(int x,int y,int l,int r){
    if(l==r)
        return l;
    int mid=(l+r)>>1;
    if((l^k)>(r^k))
        if(c[lc[y]]-c[lc[x]])
            return ask(lc[x],lc[y],l,mid);
        else 
            return ask(rc[x],rc[y],mid+1,r);
    else
        if((c[rc[y]]-c[rc[x]]))
            return ask(rc[x],rc[y],mid+1,r);
        else
            return ask(lc[x],lc[y],l,mid);
}
int main(){
    n=read();m=read();
    for(i=1;i<=n;i++)
        a[i]=o[i]=read(),sum=max(sum,a[i]);
    sum=1<<int(log2(sum*1.0)+1);
    sum--;
    build(rt[0],0,sum); 
    for(i=1;i<=n;i++){
        k=o[i];
        rt[i]=built(rt[i-1],0,sum);
    }
    for(i=1;i<=m;i++){
        tx=read();ty=read();k=read();
        cout<<(k^ask(rt[tx-1],rt[ty],0,sum))<<endl;
    }
    return 0;
}
不偏移

   然后考虑偏移.这个复杂度要变成m*log2n了.这主要是由于偏移后由判断一整个区间[x,y]变成了判断区间[x-d,y-d]内是否有数,这个过程需要我们向下寻找了,每次最坏要向下查logn次,总的复杂度就要再乘上logn.

  代码还是不难写的,只需要把上面的if内的判断改成一个函数就行.

using namespace std;
int i,k,tx,ty,d;
int n,m,a[10000],o[10000],c[1000],sum;
int tot,rt[1000],lc[1000],rc[1000];
void build(int &now,int l,int r){
    tot++;
    now=tot;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    build(lc[now],l,mid);
    build(rc[now],mid+1,r);
    return ;
}
int built(int now,int l,int r){
    tot++;
    int t=tot;
    lc[t]=lc[now];rc[t]=rc[now];c[t]=c[now]+1;
    if(l==r)
        return t;
    int mid=(l+r)>>1;
    if(k<=mid)lc[t]=built(lc[t],l,mid);
    else      rc[t]=built(rc[t],mid+1,r);
    return t;
}
bool check(int x,int y,int tl,int tr,int l,int r)
{
    if(l==r||(tl<=l&&r<=tr))
        return c[y]-c[x];
    int mid=(l+r)>>1;
    return (tl<=mid&&check(lc[x],lc[y],tl,tr,l,mid)||(tr>mid&&check(rc[x],rc[y],tl,tr,mid+1,r)));//运用短路的性质减少代码与时间
}
int ask(int x,int y,int l,int r){
    if(l==r)
        return l;
    int mid=(l+r)>>1;
    if((l^k)>(r^k))
        //if(c[lc[y]]-c[lc[x]])
        if(check(rt[tx-1],rt[ty],max(l-d,0),max(mid-d,0),0,sum) )
            return ask(lc[x],lc[y],l,mid);
        else 
            return ask(rc[x],rc[y],mid+1,r);
    else
        //if((c[rc[y]]-c[rc[x]]))
        if(check(rt[tx-1],rt[ty],max(mid+1-d,0),max(r-d,0),0,sum))
            return ask(rc[x],rc[y],mid+1,r);
        else
            return ask(lc[x],lc[y],l,mid);
}
int main(){
    n=read();m=read();
    for(i=1;i<=n;i++)
        a[i]=o[i]=read(),sum=max(sum,a[i]);
    sum=1<<int(log2(sum*1.0)+1);
    sum=sum<<1;
    sum--;
    //cout<<sum<<endl;
    build(rt[0],0,sum); 
    for(i=1;i<=n;i++){
        k=o[i];
        rt[i]=built(rt[i-1],0,sum);
    }
    for(i=1;i<=m;i++){
        tx=read();ty=read();k=read();d=read();
        cout<<(k^(ask(rt[tx-1],rt[ty],0,sum)))<<endl;
    }
    return 0;
}
带偏移
原文地址:https://www.cnblogs.com/qywyt/p/10211215.html