1. 程式人生 > 實用技巧 >【題解】[Scoi2010]序列操作

【題解】[Scoi2010]序列操作

題目連結

Statement

給定一個長為 \(n\)01 序列,要求實現 \(5\) 種操作:

  • 0 a b\([a,b]\) 全部設為 \(0\) .
  • 1 a b\([a,b]\) 全部設為 \(1\) .
  • 2 a b 將區間所有數取反 。
  • 3 a b 詢問區間內 \(1\) 的個數。
  • 4 a b 詢問區間內連續 \(1\) 的個數。

Solution

頂風作案寫DS.jpg

線段樹噁心題,但是很經典,碼得也算能看,就拿出來當板子了。

維護字首 \(0/1\) 、字尾 \(0/1\) 、連續 \(0/1\) 的個數,和區間中 \(0/1\) 的個數。注意覆蓋標記優先順序高於翻轉標記。

線段樹噁心題,碼就完了。實在不行,重構就是了

好在碼得並不慢,調得也很快,調過樣例之後一遍就過拍了(本地資料手動 diff )。

你谷開 O2 跑進了第二頁,BZOJ 沒開 O2 卡在第一頁底端/youl

希望下次寫結構體函式的時候:

\(\huge 真的不要再忘記return了\)

Code

//Author: RingweEH
//菜雞REH頂風作案
const int N=1e5+10;
int n,q,a[N];
struct SegmentTree
{
    struct Node
    {
        int cntw,cntb,lcntw,rcntw,lcntb,rcntb,mxw,mxb;
        //w個數,b個數,左/右數w個數,左/右數b個數,連續w/b最大值
        Node( int _cntw=0,int _cntb=0,int _lcntw=0,int _lcntb=0,
            int _rcntw=0,int _rcntb=0,int _mxw=0,int _mxb=0 )
        {
            cntw=_cntw; cntb=_cntb; lcntw=_lcntw; lcntb=_lcntb;
            rcntb=_rcntb; rcntw=_rcntw; mxw=_mxw; mxb=_mxb;
        }
        Node operator + ( const Node &tmp ) const
        {
            Node res;
            res.cntw=cntw+tmp.cntw; res.cntb=cntb+tmp.cntb;
            res.lcntw=cntb ? lcntw : cntw+tmp.lcntw;
            res.lcntb=cntw ? lcntb : cntb+tmp.lcntb;
            res.rcntw=tmp.cntb ? tmp.rcntw : tmp.cntw+rcntw;
            res.rcntb=tmp.cntw ? tmp.rcntb : tmp.cntb+rcntb;
            res.mxw=max( max(mxw,tmp.mxw),rcntw+tmp.lcntw );
            res.mxb=max( max(mxb,tmp.mxb),rcntb+tmp.lcntb );
            return res;
        }
    }tr[N<<2];
    int len[N<<2],tag1[N<<2],tag2[N<<2];
    //區間長度,區間賦值標記(-1/0/1),區間取反(0/1)
    void Operate( int pos,int opt ) 
    {
        Node &te=tr[pos];
        if ( opt==0 )   //reset( 0 )
        {
            tag2[pos]=tag1[pos]=0; int t=len[pos];
            te=Node( 0,t,0,t,0,t,0,t );
        }
        if ( opt==1 )   //reset( 1 )
        {
            tag2[pos]=0,tag1[pos]=1; int t=len[pos];
            te=Node( t,0,t,0,t,0,t,0 );
        }
        if ( opt==2 )   //flip
        {
            tag2[pos]^=1; 
            swap( te.cntw,te.cntb ); swap( te.mxb,te.mxw );
            swap( te.lcntw,te.lcntb ); swap( te.rcntw,te.rcntb );
        }
    }
    void Pushdown( int pos )
    {
        if ( tag1[pos]>-1 ) 
            Operate( pos<<1,tag1[pos] ),Operate( pos<<1|1,tag1[pos] );
        if ( tag2[pos] ) 
            Operate( pos<<1,2 ),Operate( pos<<1|1,2 );
        tag1[pos]=-1; tag2[pos]=0; 
    }
    void Modify( int pos,int L,int R,int l,int r,int val )
    {
        if ( r<L || R<l ) return;
        if ( l<=L && R<=r ) { Operate( pos,val ); return; }
        Pushdown( pos ); int mid=(L+R)>>1;
        Modify( pos<<1,L,mid,l,r,val ); Modify( pos<<1|1,mid+1,R,l,r,val );
        tr[pos]=tr[pos<<1]+tr[pos<<1|1];
    }
    Node Query( int pos,int L,int R,int l,int r )
    {
        if ( r<L || R<l ) return Node();
        if ( l<=L && R<=r ) return tr[pos];
        Pushdown( pos ); int mid=(L+R)>>1;
        Node lc=Query(pos<<1,L,mid,l,r),rc=Query(pos<<1|1,mid+1,R,l,r);
        return lc+rc;
    }
    void Build( int pos,int L,int R )
    {
        len[pos]=R-L+1; tag1[pos]=-1;
        if ( L==R )
        {
            int t=a[L]; tr[pos]=Node(t,t^1,t,t^1,t,t^1,t,t^1 );
            return;
        }
        int mid=(L+R)>>1;
        Build( pos<<1,L,mid ); Build( pos<<1|1,mid+1,R );
        tr[pos]=tr[pos<<1]+tr[pos<<1|1];
    }
    int Answer( int l,int r,int opt )
    {
        Node res=Query( 1,1,n,l,r );
        if ( opt==3 ) return res.cntw;
        else return res.mxw;
    }
    //w,b,lw,lb,rw,rb,mw,mb;
}Tr;

int main()
{
    n=read(); q=read();
    for ( int i=1; i<=n; i++ )
        a[i]=read();

    Tr.Build(1,1,n);
    while ( q-- )
    {
        int opt=read(),l=read()+1,r=read()+1;
        if ( opt<3 ) Tr.Modify( 1,1,n,l,r,opt );
        else printf( "%d\n",Tr.Answer(l,r,opt) );
    }

    return 0;
}