1. 程式人生 > >【BZOJ4571】[SCOI2016] 美味(主席樹)

【BZOJ4571】[SCOI2016] 美味(主席樹)

點此看題面

大致題意: 給你一個序列\(a\),然後每次詢問\(max_{i=l}^r(a_i+x)\ xor\ b\)


大致思路

首先,我們要知道一個簡單的性質:位運算時位與位之間是互不影響的

而且,要注意:二進位制數比大小先看高位

所以,我們可以從高位往低位列舉,每次判斷當前位置是否可以選擇為\(1\),且能選儘量選。

那麼如何判斷當前位是否可以選擇為\(1\)呢?


判斷當前位是否可以選擇為\(1\)

假設當前處理到第\(i\)位,且當前答案為\(ans\)

由於我們是從高位往低位列舉的,所以第\(i+1\)\(\sim\)\(17\)位(\(log_2100000≈17\)

)實際上已經確定了。

因為我們要儘量讓當前位置為\(1\),所以就要讓最後取值範圍為\([ans+2^i,ans+2^{i+1}-1]\),不難發現,這段區間內的數第\(i\)位都是\(1\)

既然我們要讓\((a_i+x)\text{^}b\in[ans+2^i,ans+2^{i+1}-1]\),由於\(b\)是定值,不難想到將\(\in\)左邊式子中的\(b\)移到右邊去。

由於\(\text{^}\)運算的性質:\(a\text{^}b=c=>a=b\text{^}c\),可得\(a_i+x\in[(ans+2^i)\text{^}b,(ans+2^{i+1}-1)\text{^}b]\)

注意,由於第\(1\sim i\)位的數字我們是不管的,所以我們依然要將左邊界第\(1\sim i\)位全部改為\(0\),右邊界第\(1\sim i\)位全部改為\(1\)

即左邊界\(L=(((ans+2^i)\text{^}b)>>i)<<i\),右邊界\(R=((((ans+2^{i+1}-1)\text{^}b)>>i)<<i)+2^i-1\)

兩邊同時減去\(x\),得:\(a_i\in[L-x,R-x]\)

也就是說,如果\([l,r]\)範圍內存在一個\(i\)滿足\(a_i\in[L-x,R-x]\),就可以將\(ans\)

加上\(2^i\)了。

而這可以用主席樹來進行維護。

於是這道題就做完了。


程式碼

#include<bits/stdc++.h>
#define N 200000
#define Log 17
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,a[N+5];
class Class_FIO
{
    private:
        #define Fsize 100000
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
        #define pc(ch) (void)(FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
        int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize];
    public:
        Class_FIO() {A=B=Fin;}
        inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));}
        inline void writeln(int x) {if(!x) return pc('0'),pc('\n');while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);pc('\n');}
        inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;}
}F;
class Class_ChairmanTree//主席樹
{
    private:
        #define TreeSize 100000
        #define LogN 20
        int tot,Root[N+5];
        struct Tree
        {
            int Son[2],Size;
        }node[N*LogN+5];
        inline void ins(int l,int r,int &rt,int lst,int val)
        {
            if(node[rt=++tot]=node[lst],++node[rt].Size,!(l^r)) return;
            register int mid=l+r>>1;
            val<=mid?ins(l,mid,node[rt].Son[0],node[lst].Son[0],val):ins(mid+1,r,node[rt].Son[1],node[lst].Son[1],val);
        }
        inline bool qry(int l,int r,int rt1,int rt2,int ql,int qr)
        {
            if(ql<=l&&r<=qr) return node[rt2].Size^node[rt1].Size;
            register int mid=l+r>>1;
            return (ql<=mid&&qry(l,mid,node[rt1].Son[0],node[rt2].Son[0],ql,qr))||(qr>mid&&qry(mid+1,r,node[rt1].Son[1],node[rt2].Son[1],ql,qr));
        }
    public:
        inline void Init(int len,int *data) {for(register int i=1;i<=len;++i) ins(0,TreeSize,Root[i],Root[i-1],data[i]);}//初始化
        inline bool Query(int l,int r,int ql,int qr) {return Gmax(ql,0),Gmin(qr,TreeSize),ql<=qr?qry(0,TreeSize,Root[l-1],Root[r],ql,qr):false;}//詢問第l~r個元素中是否有數字在[ql,qr]範圍內
}ChairmanTree;
int main()
{
    register int i,query_tot,x,y,l,r,L,R,t,ans;
    for(F.read(n),F.read(query_tot),i=1;i<=n;++i) F.read(a[i]);
    for(ChairmanTree.Init(n,a);query_tot;--query_tot)
    {
        for(F.read(x),F.read(y),F.read(l),F.read(r),ans=0,i=Log;~i;--i)//從高位向低位列舉
            L=ans+(1<<i),R=L+(t=(1<<i)-1),ChairmanTree.Query(l,r,(((L^x)>>i)<<i)-y,(((R^x)>>i)<<i)+t-y)&&(ans|=1<<i);//判斷能否選擇為1
        F.writeln(ans);//輸出答案
    }
    return F.clear(),0;
}