BZOJ5312 冒險(勢能線段樹)

BZOJ題目傳送門php

表示蒟蒻並不能一眼看出來這是個勢能線段樹。c++

不過仔細想一想也並不是難以理解,感性理解一下,在一個區間裏又與又或,那麼原本不相同的位也會漸漸相同,線段樹每一個葉子節點最多修改\(\log a\)次(\(a\)爲值域)。數組

那麼,咱們作區間修改的時候,進行判斷:若是這一次修改對區間裏全部數的影響都是同樣的,那麼直接在當前位置放懶標記。ui

如何判斷呢?又是一個位運算技巧:維護區間與和區間或,二者的異或即爲區間內存在不一樣的位集。那麼只有這些位集不會被與上0、或上1,才能夠放懶標記。spa

至於又與又或很麻煩,咱們定義標記\((la,lo)\)表示整個區間都&la|locode

標記的合併手推一下就行了,\((la,lo)+(na,no)=(la\&na,lo\&na|no)\)ip

複雜度\(n\log n\log a\),果真維護的東西一多數組版線段樹的常數就大起來了。。。內存

#include<bits/stdc++.h>
#define RG register
#define R RG int
#define G if(++ip==ie)fread(ip=buf,1,N,stdin)
using namespace std;
const int N=1<<19,S=(1<<21)-1;
char buf[N],*ie=buf+N,*ip=ie-1;
int na,no,sa[N],so[N],la[N],lo[N],mx[N];
inline int in(){
    G;while(*ip<'-')G;
    R x=*ip&15;G;
    while(*ip>'-'){x*=10;x+=*ip&15;G;}
    return x;
}
#define Pushup                                  \
    sa[x]=sa[lc]&sa[rc];                        \
    so[x]=so[lc]|so[rc];                        \
    mx[x]=max(mx[lc],mx[rc])
#define Pushdn                                  \
    if(la[x]!=S||lo[x]){                        \
        pusht(lc,la[x],lo[x]);                  \
        pusht(rc,la[x],lo[x]);                  \
        la[x]=S;lo[x]=0;                        \
    }
inline void pusht(R x,R a,R o){//合併標記並更新信息
    la[x]&=a;(lo[x]&=a)|=o;
    (so[x]&=a)|=o;(sa[x]&=a)|=o;(mx[x]&=a)|=o;
}
void build(R x,R l,R r){
    la[x]=S;
    if(l==r){
        mx[x]=sa[x]=so[x]=in();return;
    }
    R m=(l+r)>>1,lc=x<<1,rc=lc|1;
    build(lc,l,m);build(rc,m+1,r);
    Pushup;
}
void upd(R x,R l,R r,R s,R e){
    if(l==s&&r==e&&!((sa[x]^so[x])&(~na|no)))//判斷是否影響一致
        return pusht(x,na,no);
    R m=(l+r)>>1,lc=x<<1,rc=lc|1;
    Pushdn;
    if(e<=m)upd(lc,l,m,s,e);
    else if(s>m)upd(rc,m+1,r,s,e);
    else upd(lc,l,m,s,m),upd(rc,m+1,r,m+1,e);
    Pushup;
}
int qry(R x,R l,R r,R s,R e){
    if(l==s&&r==e)return mx[x];
    R m=(l+r)>>1,lc=x<<1,rc=lc|1;
    Pushdn;
    if(e<=m)return qry(lc,l,m,s,e);
    if(s>m)return qry(rc,m+1,r,s,e);
    return max(qry(lc,l,m,s,m),qry(rc,m+1,r,m+1,e));
}
int main(){
    R n=in(),m=in(),op,l,r;
    build(1,1,n);
    while(m--){
        op=in();l=in();r=in();
        if(op==1)na=in(),no=0,upd(1,1,n,l,r);//或上0仍是原來的數
        if(op==2)na=S,no=in(),upd(1,1,n,l,r);//與上全1仍是原來的數
        if(op==3)printf("%d\n",qry(1,1,n,l,r));
    }
    return 0;
}
相關文章
相關標籤/搜索