「雅礼集训 2018 Day7」A 题解 (线段树,二进制)
题目简介
给定一个长度为 \(n\) 的序列 \(A_i\),下标从 \(1\) 开始。对其依次进行 \(m\) 次操作或询问,分为 \(3\) 种类型:
1 l r x
:将 \(A_{l...r}\) 中每个元素二进制与上一个数 \(x\)2 l r x
:将 \(A_{l...r}\) 中每个元素二进制或上一个数 \(x\)3 l r
:求 \(A_{l...r}\) 中的最小值
分析
线段树维护三个变量:\(va\),\(vo\) 和 \(mn\) 。
\(va\) 表示区间和,\(vo\) 表示区间或,\(mn\) 表示区间最小值。
设 \(\mbox{S}(x)\) 表示 \(x\) 中二进制位为 \(1\) 的位置的集合,\(x(b)\) 表示 \(x\) 二进制下第 \(b\) 位。
对于 \(1\) 操作来看,及 \([l,r]\ \mbox{and}\ k\),有以下结论:
-
若 \(vo\ \mbox{and}\ k = vo\),则区间中所有数二进制位为 \(1\) 的,\(k\) 的相应位置也是 \(1\),此时该操作无效。
道理很简单,\(vo\ \mbox{and}\ k = vo\),则\(\mbox{S}(vo)\subseteq\mbox{S}(k)\) ,而 \(\forall x\in[l,r],\ \mbox{S}(x)\subseteq\mbox{S}(vo)\),所以 \(\mbox{S}(x)\subseteq\mbox{S}(k)\)
-
若 \(vo\ \mbox{and}\ k = va\ \mbox{and}\ k = t\),则区间中所有数操作后相等。
显然,\(\mbox{S}(va)\subseteq\mbox{S}(vo)\)。而 \(vo\ \mbox{and}\ k = va\ \mbox{and}\ k\),所以一旦\(b\in\mbox{S}(vo),b\notin\mbox{S}(va)\),则 \(k(b)=0\)。
对于区间中一个数 \(x\),
- 如果 \(x(b)=1\),则 \(b\in\mbox{S}(va)\) ,\(x(b)\ \mbox{and}\ k=t(b)\)
- 否则如果 \(x(b)=0\),若\(b\in\mbox{S}(vo)\),则 \(k(b) =0\),\(x(b)\ \mbox{and}\ k=t(b)=0\),
- 否则 \(b\notin\mbox{S}(vo)\),\(x(b)\ \mbox{and}\ k=t(b)\)
-
不断递归下去,一定有一个子区间满足上述两个条件,极限当 \(l=r\) 时,一定满足第二个条件。
对于 \(2\) 操作来看,及\([l,r]\ \mbox{or}\ k\),有一下结论:
-
若 \(va\ \mbox{or}\ k=va\),,则区间中所有数二进制位全为 \(1\) 的,\(k\) 的相应位置也是 \(1\),此时该操作无效。
证明方法类似上述第一个结论。\(\mbox{S}(k)\subseteq\mbox{S}(va),\mbox{S}(va)\subseteq\mbox{S}(x)\) 故 \(\mbox{S}(k)\subseteq\mbox{S}(x)\)
-
若 \(va\ \mbox{or}\ k=vo\ \mbox{or}\ k = t\),则区间中所有数操作后相等。
证明方法类似上述第二个结论,不再赘述。
-
不断递归下去,一定有一个子区间满足上述两个条件,极限当 \(l=r\) 时,一定满足第二个条件。
时间复杂度玄学,笔者不会证明/kk
\(AC\ Code\)
#include<cstdio>
#include<iostream>
using namespace std;
const int Maxn=5e5+5;
const int E=0x7fffffff;
struct segtree{
int l,r;
int va,vo,mn;
int ta,to;
}tr[Maxn<<2];
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline void pushup(int x){
tr[x].va=tr[ls(x)].va&tr[rs(x)].va;
tr[x].vo=tr[ls(x)].vo|tr[rs(x)].vo;
tr[x].mn=min(tr[ls(x)].mn,tr[rs(x)].mn);
}
inline void tag_and(int x,int c){
tr[x].va&=c,tr[x].vo&=c,tr[x].mn&=c;
tr[x].ta&=c,tr[x].to&=c;
}
inline void tag_or (int x,int c){
tr[x].va|=c,tr[x].vo|=c,tr[x].mn|=c;
tr[x].ta|=c,tr[x].to|=c;
}
inline void pushdown(int x){
if(tr[x].ta!=E)tag_and(ls(x),tr[x].ta),tag_and(rs(x),tr[x].ta);
if(tr[x].to!=0)tag_or (ls(x),tr[x].to),tag_or (rs(x),tr[x].to);
tr[x].ta=E,tr[x].to=0;
}
int a[Maxn];
void build(int x,int L,int R){
tr[x].l=L,tr[x].r=R;tr[x].ta=E;
if(L==R){tr[x].va=tr[x].vo=tr[x].mn=a[L];return ;}
int mid=L+R>>1;
build(ls(x),L,mid);
build(rs(x),mid+1,R);
pushup(x);
}
void modify_and(int x,int ql,int qr,int d){
if((tr[x].vo&d)==tr[x].vo)return ;
if(ql<=tr[x].l&&tr[x].r<=qr)
if((tr[x].vo&d)==(tr[x].va&d))return tag_and(x,d);
pushdown(x);
int mid=tr[x].l+tr[x].r>>1;
if(ql<=mid)modify_and(ls(x),ql,qr,d);
if(qr> mid)modify_and(rs(x),ql,qr,d);
pushup(x);
}
void modify_or (int x,int ql,int qr,int d){
if((tr[x].va|d)==tr[x].va)return ;
if(ql<=tr[x].l&&tr[x].r<=qr)
if((tr[x].va|d)==(tr[x].vo|d))return tag_or (x,d);
pushdown(x);
int mid=tr[x].l+tr[x].r>>1;
if(ql<=mid)modify_or (ls(x),ql,qr,d);
if(qr> mid)modify_or (rs(x),ql,qr,d);
pushup(x);
}
int query(int x,int ql,int qr){
if(ql<=tr[x].l&&tr[x].r<=qr)return tr[x].mn;
pushdown(x);
int mid=tr[x].l+tr[x].r>>1;
if(qr<=mid)return query(ls(x),ql,qr);
if(ql> mid)return query(rs(x),ql,qr);
return min(query(ls(x),ql,qr),query(rs(x),ql,qr));
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
int n,m;cin>>n>>m;
for(int i=1;i<=n;++i)cin>>a[i];
build(1,1,n);
while(m--){
int op,l,r,d;cin>>op>>l>>r;
if(op==1)cin>>d,modify_and(1,l,r,d);
if(op==2)cin>>d,modify_or (1,l,r,d);
if(op==3)cout<<query(1,l,r)<<'\n';
}
return 0;
}