[BZOJ]5312: 冒險 線段樹 複雜度分析
阿新 • • 發佈:2018-12-24
Description
Kaiser終於成為冒險協會的一員,這次冒險協會派他去冒險,他來到一處古墓,卻被大門上的守護神擋住了去路,守護神給出了一個問題,
只有答對了問題才能進入,守護神給出了一個自然數序列a,每次有一下三種操作。
1,給出l,r,x,將序列l,r之間的所有數都 and x
2,給出l,r,x,將序列l,r之間的所有數都 or x
3,給出l,r,詢問l,r之間的最大值
題解:
半年前不會做這道題……但是現在會做了。
我們先只考慮or操作。
定義一個線段樹節點的勢能(即操作次數)為這個區間的所有數有幾個位不是完全一樣的。
如果現在一個區間or一個數,對所有數的影響是一樣的,那麼對這個區間的操作就可以變為區間加。顯然一次or操作如果不能直接區間加,那麼這個區間至少會有一個位全部變為
,即這個區間的勢能減少
。由於勢能是隻會遞減的,所以複雜度為
。(到一個節點至少會使它的勢能
)。
如果考慮兩種操作,勢能不再是遞減的,但是由於一次and操作只會使
個區間的勢能恢復為
,所以總勢能即複雜度變為
,可以通過。
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=200010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,a[Maxn];
struct Seg{int l,r,lc,rc,Or,And,tag,mx;}tr[Maxn<<1];
int len=0;
void Add(int x,int v)
{
tr[x].mx+=v;tr[x].And+=v;tr[x].Or+=v;tr[x].tag+=v;
}
void up(int x)
{
int lc=tr[x].lc,rc=tr[x].rc;
tr[x].Or=(tr[lc].Or|tr[rc].Or);
tr[x].And=(tr[lc].And&tr[rc].And);
tr[x].mx=max(tr[lc].mx,tr[rc].mx);
}
void down(int x)
{
if(tr[x].tag)Add(tr[x].lc,tr[x].tag),Add(tr[x].rc,tr[x].tag),tr[x].tag=0;
}
void build(int l,int r)
{
int x=++len;
tr[x].l=l;tr[x].r=r;tr[x].tag=0;
if(l==r){tr[x].mx=tr[x].Or=tr[x].And=a[l];return;}
int mid=l+r>>1;
tr[x].lc=len+1,build(l,mid);
tr[x].rc=len+1,build(mid+1,r);
up(x);
}
int query(int x,int l,int r)
{
if(tr[x].l==l&&tr[x].r==r)return tr[x].mx;
int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
down(x);
if(r<=mid)return query(lc,l,r);
if(l>mid)return query(rc,l,r);
return max(query(lc,l,mid),query(rc,mid+1,r));
}
void AND(int x,int l,int r,int v)
{
if(l<=tr[x].l&&tr[x].r<=r)
{
bool flag=true;int t=0;
for(int i=20;i>=0;i--)
if(!((1<<i)&v))
{
if(!(((1<<i)&tr[x].And)||(!((1<<i)&tr[x].Or)))){flag=false;break;}
if((1<<i)&tr[x].And)t-=(1<<i);
}
if(flag)
{
Add(x,t);
return;
}
}
int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
down(x);
if(l<=mid)AND(lc,l,r,v);
if(r>mid)AND(rc,l,r,v);
up(x);
}
void OR(int x,int l,int r,int v)
{
if(l<=tr[x].l&&tr[x].r<=r)
{
bool flag=true;int t=0;
for(int i=20;i>=0;i--)
if((1<<i)&v)
{
if(!(((1<<i)&tr[x].And)||(!((1<<i)&tr[x].Or)))){flag=false;break;}
if(!((1<<i)&tr[x].Or))t+=(1<<i);
}
if(flag)
{
Add(x,t);
return;
}
}
int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
down(x);
if(l<=mid)OR(lc,l,r,v);
if(r>mid)OR(rc,l,r,v);
up(x);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)a[i]=read();
build(1,n);
while(m--)
{
int op=read(),l=read(),r=read();
if(op==1)AND(1,l,r,read());
else if(op==2)OR(1,l,r,read());
else printf("%d\n",query(1,l,r));
}
}