【题解】序列操作 & CPU监控

序列操作

题目链接

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;
}

CPU监控

题目链接

Statement

对于一个序列,要求支持操作:

  • Q X Y 询问区间 \([X,Y]\) 的最大值
  • A X Y 询问区间 \([X,Y]\) 的历史最大值
  • P X Y Z 区间 \([X,Y]\) 增加为 \(Z\)
  • C X Y Z 区间 \([X,Y]\) 推平为 \(Z\)

Solution

坑点:如果照常写,会发现有些 tag 还没更新就被覆盖了,然后就出大问题。所以要多记录一个“距离上次下传到目前的增加和推平 \(\max\) ”。

其他就仔细点就好了。

重构永远的神!

//Author: RingweEH
#define lson pos<<1
#define rson pos<<1|1
const int N=1e5+10,INF=0x3f3f3f3f;
struct Node
{
	int val,add,assign,lasv,lasad,lasas;
};
void bmax( int &x,int y ) { x=(x>y) ? x : y; }
struct SegmentTree
{
	Node tr[N<<2];
	void Pushup( int pos )
	{
		tr[pos].val=max( tr[lson].val,tr[rson].val );
		tr[pos].lasv=max( tr[lson].lasv,tr[rson].lasv );
	}
	void Upd_Add( int pos,int add,int lasad )
	{
		if ( tr[pos].assign!=-INF )		//有区间赋值操作
		{
			bmax( tr[pos].lasas,tr[pos].assign+lasad );	//遗留赋值和当前赋值+遗留add取max
			bmax( tr[pos].lasv,tr[pos].val+lasad );		//遗留mx和当前mx+遗留add取max
			tr[pos].assign+=add; tr[pos].val+=add;
		}
		else	//没有区间赋值
		{
			bmax( tr[pos].lasad,tr[pos].add+lasad );	//遗留add和当前add+遗留add取mx
			bmax( tr[pos].lasv,tr[pos].val+lasad );		//遗留mx和当前mx+遗留add取mx
			tr[pos].add+=add; tr[pos].val+=add;
		}
	}
	void Upd_Assign( int pos,int ass,int lasas )
	{
		bmax( tr[pos].lasas,lasas );	//和当前遗留ass取max
		bmax( tr[pos].lasv,lasas );		//更新遗留mx
		tr[pos].assign=ass; tr[pos].val=ass;	//更新当前ass和mx
	}
	void Pushdown( int pos )
	{
		Upd_Add( lson,tr[pos].add,tr[pos].lasad );	//下放add标记
		Upd_Add( rson,tr[pos].add,tr[pos].lasad );	
		tr[pos].add=tr[pos].lasad=0;
		if ( tr[pos].assign!=-INF )		//下放ass标记,因为优先级高
		{
			Upd_Assign( lson,tr[pos].assign,tr[pos].lasas );
			Upd_Assign( rson,tr[pos].assign,tr[pos].lasas );
			tr[pos].assign=tr[pos].lasas=-INF;		//注意不是赋值成0,因为有负数
		}
	}
	void Modify_Add( int pos,int L,int R,int l,int r,int val )
	{
		if ( L>r || R<l ) return;
		if ( l<=L && R<=r ) { Upd_Add( pos,val,val ); return; }
		Pushdown( pos ); int mid=(L+R)>>1;
		Modify_Add( lson,L,mid,l,r,val ); Modify_Add( rson,mid+1,R,l,r,val );
		Pushup( pos );
	}
	void Modify_Ass( int pos,int L,int R,int l,int r,int val )
	{
		if ( L>r || R<l ) return;
		if ( l<=L && R<=r ) { Upd_Assign( pos,val,val ); return; }
		Pushdown( pos ); int mid=(L+R)>>1;
		Modify_Ass( lson,L,mid,l,r,val ); Modify_Ass( rson,mid+1,R,l,r,val );
		Pushup( pos );
	}
	int Query( int pos,int L,int R,int l,int r,int opt )
	{
		if ( L>r || R<l ) return -INF;
		if ( l<=L && R<=r ) return opt ? tr[pos].lasv : tr[pos].val;
		Pushdown( pos ); int mid=(L+R)>>1;
		return max( Query( lson,L,mid,l,r,opt ),Query( rson,mid+1,R,l,r,opt ) );
	}
	void Build( int pos,int l,int r )
	{
		tr[pos].assign=tr[pos].lasas=-INF;
		if ( l==r )
		{
			tr[pos].val=tr[pos].lasv=read(); return;
		}
		int mid=(l+r)>>1;
		Build( lson,l,mid ); Build( rson,mid+1,r );
		Pushup( pos );
	}
}Tr;
char ch[5];

int main()
{
	int n=read(); Tr.Build(1,1,n); int q=read();
	while( q-- )
	{
		scanf( "%s",ch ); int x=read(),y=read(),z;
		if ( ch[0]=='Q' ) printf( "%d\n",Tr.Query(1,1,n,x,y,0) );
		else if ( ch[0]=='A' ) printf( "%d\n",Tr.Query(1,1,n,x,y,1) );
		else if ( ch[0]=='P' ) z=read(),Tr.Modify_Add(1,1,n,x,y,z);
		else z=read(),Tr.Modify_Ass(1,1,n,x,y,z);
	}
    return 0;
}
posted @ 2020-12-27 19:41  MontesquieuE  阅读(155)  评论(0编辑  收藏  举报