【题解】序列操作 & 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;
}
大地也该是从一片类似的光明中冒出来的。