首先把
∧
∧
看成把某些位强制变为
0
0
,把看成把某些位强制变为
1
1
。那么对于一次修改,如果一个区间内的数这些位都相同就打标记,否则暴力处理左右区间即可。
先证明这样做的复杂度是对的,设,那么
f(root)
f
(
r
o
o
t
)
一开始是
O(20N)
O
(
20
N
)
的,每次修改最多增加
O(20logN)
O
(
20
log
N
)
,而每次访问一个新节点
f(root)
f
(
r
o
o
t
)
至少减少
1
1
,所以复杂度是的。
至于打标记,可以用一个与标记和一个或标记来表示(注意认为规定先后顺序),判断一些位是否全相同,可以维护区间与和、区级或和,与和为
1
1
或者或和为的位就是全相同的了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define mid (l+r>>1)
#define N 200010
using namespace std;
const int R=(1<<20)-1;
int n,q,a[N];
int read()
{
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
struct tree
{
int mx,h,y,bh,by;
tree *ls,*rs;
tree(){h=0;y=R;}
void update()
{
mx=max(ls->mx,rs->mx);
bh=ls->bh|rs->bh;
by=ls->by&rs->by;
}
void cal(int dh,int dy)
{
mx=(mx&dy)|dh;bh=(bh&dy)|dh;by=(by&dy)|dh;
y=(y&dy);h=(h&dy)|dh;
}
void pushdown()
{
ls->cal(h,y);
rs->cal(h,y);
h=0;y=R;
}
void build(int l,int r)
{
if(l==r) {mx=bh=by=a[l];return ;}
(ls=new tree)->build(l,mid);
(rs=new tree)->build(mid+1,r);
update();
}
void mdf(int l,int r,int lx,int rx,int dh,int dy)
{
if(l==lx&&r==rx)
{
int t=(by|(R^bh));
if(((dh|t)==t)&&(((R^dy)|t)==t)) {cal(dh,dy);return ;}
}
pushdown();
if(rx<=mid) ls->mdf(l,mid,lx,rx,dh,dy);
else if(lx>mid) rs->mdf(mid+1,r,lx,rx,dh,dy);
else ls->mdf(l,mid,lx,mid,dh,dy),rs->mdf(mid+1,r,mid+1,rx,dh,dy);
update();
}
int qry(int l,int r,int lx,int rx)
{
if(l==lx&&r==rx) return mx;
pushdown();
if(rx<=mid) return ls->qry(l,mid,lx,rx);
if(lx>mid) return rs->qry(mid+1,r,lx,rx);
return max(ls->qry(l,mid,lx,mid),rs->qry(mid+1,r,mid+1,rx));
}
}*xtr;
int main()
{
n=read();q=read();
for(int i=1;i<=n;i++)
a[i]=read();
(xtr=new tree)->build(1,n);
while(q--)
{
int opt=read(),l=read(),r=read(),x;
if(opt==1) xtr->mdf(1,n,l,r,0,read());
if(opt==2) xtr->mdf(1,n,l,r,read(),R);
if(opt==3) printf("%d\n",xtr->qry(1,n,l,r));
}
return 0;
}