「SCOI2016」美味
阿新 • • 發佈:2020-11-03
題意
做法
一個很新鮮的idea,由於直接建\(01\)trie沒法支援修改,不妨考慮另類做法:
列舉答案\(xor\) \(b\)(即列舉\(x_{i}+a_{j}\))的二進位制每一位是\(0\)還是\(1\),即對於最高位第\(t\)位,如果我想要這一位是\(0\)(因為\(b\)的這一位是\(1\)),那麼就判斷\([l,r]\)區間是否有\([0,2^{t}-1]\)的數字,後面的位類似。
至於判斷區間是否存在某個區間的數字,直接用動態開點線段樹搞就行了。
時間複雜度:\(O(nlog^2值域)\)
#include<cstdio> #include<cstring> #define N 210000 #define NN 4100000 using namespace std; struct node { int l,r,c/*數字個數*/; }tr[NN];int len,rt[N]; inline void updata(int x){tr[x].c+=tr[tr[x].l].c+tr[tr[x].r].c;} void link(int &x,int l,int r,int k) { if(!x)x=++len; tr[x].c++; if(l==r)return ; int mid=(l+r)>>1; if(k<=mid)link(tr[x].l,l,mid,k); else link(tr[x].r,mid+1,r,k); } void mer(int &x,int y) { if(!x || !y){x=x+y;return ;} tr[x].c+=tr[y].c; mer(tr[x].l,tr[y].l);mer(tr[x].r,tr[y].r); } bool query(int x,int y,int l,int r,int ll,int rr) { if(tr[x].c==tr[y].c)return 0; else if(l==ll && r==rr)return tr[x].c-tr[y].c>0; int mid=(l+r)>>1; if(rr<=mid)return query(tr[x].l,tr[y].l,l,mid,ll,rr); else if(mid<ll)return query(tr[x].r,tr[y].r,mid+1,r,ll,rr); else { if(!query(tr[x].l,tr[y].l,l,mid,ll,mid))return query(tr[x].r,tr[y].r,mid+1,r,mid+1,rr); return 1; } } int n,m,limit=262143; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { int x;scanf("%d",&x); link(rt[i],0,limit,x); } for(int i=2;i<=n;i++)mer(rt[i],rt[i-1]); for(int i=1;i<=m;i++) { int b,x,l,r;scanf("%d%d%d%d",&b,&x,&l,&r); int ans=0; for(int j=17;j>=0;j--) { int ll=ans,rr=ans; if(b&(1<<j))rr+=(1<<j)-1;//有 else ll+=(1<<j),rr+=(1<<(j+1))-1;//沒有 ll-=x;rr-=x; int shit; if(ll<0 && rr<0)shit=0;//完全不行 else { if(ll<0)ll=0; shit=query(rt[r],rt[l-1],0,limit,ll,rr); } ans|=(b&(1<<j))^(shit<<j); } printf("%d\n",ans^b); } return 0; }