LOJ6507 「雅禮集訓 2018 Day7」A
阿新 • • 發佈:2021-10-10
考慮線段樹維護區間或 \(o\) 和區間與 \(a\),對於區間和 \(k\) 與的操作:
- 若 \(o \operatorname{and} k=o\),則說明每個此區間有 \(1\) 的位 \(k\) 也都是 \(1\),操作在此區間沒用
- 若 \(o \operatorname{and} k=a \operatorname{and} k\),則說明 \(k\) 只在區間所有數 都有 或 都沒有 \(1\) 的位置上有 \(1\),那麼可以打標記的同時維護最小值
- 其他情況遞迴下去
或操作類似
關於複雜度,設 \(u\) 節點的勢能是 滿足區間中存在兩數的這一二進位制位不同 的二進位制位數
那麼初始勢能是 \(O(n\log n\log A)\)
而每次暴力遞迴一層,至少會減少 \(1\) 的勢能,因此複雜度 \(O(n\log n\log A)\)
#define lim 2147483647 #define N 500006 struct Node{ Node *ls,*rs; int _and,_or,min; int tand,tor; inline void pushup(){ min=std::min(ls->min,rs->min); _and=ls->_and&rs->_and;_or=ls->_or|rs->_or; } inline void And(int k){_and&=k;_or&=k;min&=k;tand&=k;tor&=k;} inline void Or(int k){_and|=k;_or|=k;min|=k;tand|=k;tor|=k;} inline void pushdown(){ ls->And(tand);ls->Or(tor); rs->And(tand);rs->Or(tor); tand=lim;tor=0; } }dizhi[N*2],*root;int tot; int a[N]; void build(Node *&tree,int l,int r){ tree=&dizhi[++tot]; tree->tand=lim; if(l==r) return tree->_and=tree->_or=tree->min=a[l],void(); int mid=(l+r)>>1; build(tree->ls,l,mid);build(tree->rs,mid+1,r); tree->pushup(); } void And(Node *tree,int l,int r,int ql,int qr,int k){ if((tree->_or&k)==tree->_or) return; if(ql<=l&&r<=qr&&(tree->_and&k)==(tree->_or&k)) return tree->And(k),void(); tree->pushdown(); int mid=(l+r)>>1; if(ql<=mid) And(tree->ls,l,mid,ql,qr,k); if(qr>mid) And(tree->rs,mid+1,r,ql,qr,k); tree->pushup(); } void Or(Node *tree,int l,int r,int ql,int qr,int k){ if((tree->_and|k)==tree->_and) return; if(ql<=l&&r<=qr&&(tree->_and|k)==(tree->_or|k)) return tree->Or(k),void(); tree->pushdown(); int mid=(l+r)>>1; if(ql<=mid) Or(tree->ls,l,mid,ql,qr,k); if(qr>mid) Or(tree->rs,mid+1,r,ql,qr,k); tree->pushup(); } int getMin(Node *tree,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr) return tree->min; tree->pushdown(); int mid=(l+r)>>1,ans=lim; if(ql<=mid) ans=getMin(tree->ls,l,mid,ql,qr); if(qr>mid) ans=std::min(ans,getMin(tree->rs,mid+1,r,ql,qr)); return ans; } int main(){ // freopen("1.in","r",stdin); int n=read(),m=read(); for(int i=1;i<=n;i++) a[i]=read(); build(root,1,n); while(m--){ int op=read(),l=read(),r=read(); if(op==1) And(root,1,n,l,r,read()); else if(op==2) Or(root,1,n,l,r,read()); else if(op==3) printf("%d\n",getMin(root,1,n,l,r)); } return 0; }