bzoj 4571 美味 —— 主席树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4571

区间找异或值最大,还带加法,可以用主席树;

可以按位考虑,然后通过加上之前已经有的答案、减去题目给的那个 x ,得到满足这一位最大的值的范围,查找一下有没有即可;

注意如果写 b&(1<<j) ,这个不仅是 0 或 1!所以用个 bool 类型存下来。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid ((l+r)>>1)
using namespace std;
int const xn=2e5+5,mx=(1<<18)-1,xm=xn*20;
int n,m,cnt,sum[xm],ls[xm],rs[xm],rt[xn];
int rd()
{
    int ret=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
    while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
    return f?ret:-ret;
}
void insert(int &x,int y,int l,int r,int v)
{
    x=++cnt;
    sum[x]=sum[y]+1; ls[x]=ls[y]; rs[x]=rs[y];
    if(l==r)return;
    if(v<=mid)insert(ls[x],ls[y],l,mid,v);
    else insert(rs[x],rs[y],mid+1,r,v);
}
int query(int x,int y,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)return sum[y]-sum[x];
    int ret=0;
    if(mid>=L)ret+=query(ls[x],ls[y],l,mid,L,R);
    if(mid<R)ret+=query(rs[x],rs[y],mid+1,r,L,R);
    return ret;
}
int main()
{
    n=rd(); m=rd();
    for(int i=1,x;i<=n;i++)
    {
        x=rd();
        insert(rt[i],rt[i-1],0,mx,x);
    }
    for(int i=1,b,x,l,r,ans;i<=m;i++)
    {
        b=rd(); x=rd(); l=rd(); r=rd(); ans=0;
        for(int j=17;j>=0;j--)
        {
            int L,R; bool k=(b&(1<<j));//bool!!
            if(k)//这一位应填0
                L=max(ans-x,0),R=min(ans-x+(1<<j)-1,mx);
            else L=max(ans-x+(1<<j),0),R=min(ans-x+(1<<(j+1))-1,mx);
            if(L<=R&&query(rt[l-1],rt[r],0,mx,L,R))ans+=(1<<j)*(k^1);
            else ans+=(1<<j)*k;
        }
        printf("%d\n",ans^b);
    }
    return 0;
}

 

posted @ 2018-09-29 19:43  Zinn  阅读(144)  评论(0编辑  收藏  举报