LOJ6507 「雅礼集训 2018 Day7」A
考虑线段树维护区间或 \(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)\),每次修改操作会增加最多 \(O(\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;
}