见过最恶心的一个线段树了。
先总结一下最近做的线段树吧。(基本都是裸的╮(╯▽╰)╭)
线段树的操作就是单点更新,区间更新,区间查询。
最基本的就是区间和,区间最值。
一般都是10W个节点,10W个询问
比较难一点的就是区间翻转,区间内的各种连续。
对付区间翻转就是弄一个翻转标记rev[],rev[i]=1的话表示这个节点的子节点全都要翻转,如果有覆盖标记的话,要保持翻转标记和覆盖标记最多同时存在一个。
区间连续的话,比如说,求区间连续1的个数最多是多少,那就搞3个数组,mx[],lx[],rx[],mx[i]表示节点i里面连续1的个数的最大值,lx[i]表示从节点i左端点开始连续1的个数。
rx[i]表示右端点开始连续1的个数(对这个题,因为有翻转操作,所以还要知道连续0的个数)。然后更新的时候mx[],lx[],rx[]都要更新
线段树更难一点的就是树套树什么的了。。
当然,更恶心的是线段树的题你不知道要用线段树做!!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<cstdlib>
#include<stack>
using namespace std;
#define inf 0x3f3f3f3f
#define eps 1e-7
#define LL long long
#define ULL unsigned long long
#define MP make_pair
#define pb push_back
#define ls ( i << 1 )
#define rs ( ls | 1 )
#define md ( ( ll[i] + rr[i] ) >> 1 )
#define PI acos( -1.0 )
#define mxn 400020
int ll[mxn], rr[mxn], add[mxn], rev[mxn], mx0[mxn], mx1[mxn], sum[mxn];
int lx0[mxn], lx1[mxn], rx0[mxn], rx1[mxn];
//ll[i],rr[i]节点i所管理的左端点和右端点
//add[i],覆盖标记,不存在为-1,0的覆盖标记为0,1的覆盖标记为1
//rev[i], 翻转标记
//mx0[i], 连续0最大个数,mx1[i],连续1的最大个数,sum[i]区间和
//lx0[i], 左端点开始连续0的个数,lx1[i],左端点开始连续1的个数
//rx0[i], rx1[i]类似
int a[mxn];
int n, m;
int len( int i ) { //返回区间长度
return rr[i] - ll[i] + 1;
}
void Up( int i ) { //向上更新,就是通过左儿子和右儿子更新自己
sum[i] = sum[ls] + sum[rs];
mx0[i] = max( mx0[ls], mx0[rs] );
mx1[i] = max( mx1[ls], mx1[rs] );
bool flag1 = rx1[ls], flag2 = lx1[rs];
if( flag1 && flag2 ) {
mx1[i] = max( mx1[i], rx1[ls] + lx1[rs] );//
if( lx1[ls] == len( ls ) )
lx1[i] = len( ls ) + lx1[rs];
else
lx1[i] = lx1[ls];
if( rx1[rs] == len( rs ) )
rx1[i] = len( rs ) + rx1[ls];
else
rx1[i] = rx1[rs];
lx0[i] = lx0[ls];
rx0[i] = rx0[rs];
return;//
}
if( !flag1 && !flag2 ) {
mx0[i] = max( mx0[i], rx0[ls] + lx0[rs] );
if( lx0[ls] == len( ls ) )
lx0[i] = len( ls ) + lx0[rs];
else
lx0[i] = lx0[ls];
if( rx0[rs] == len( rs ) )
rx0[i] = len( rs ) + rx0[ls];
else
rx0[i] = rx0[rs];
lx1[i] = lx1[ls];
rx1[i] = rx1[rs];
return;
}
lx0[i] = lx0[ls];
rx0[i] = rx0[rs];
lx1[i] = lx1[ls];
rx1[i] = rx1[rs];
}//
void ad( int i, int d ) { //把区间覆盖为d
add[i] = d, rev[i] = 0;
sum[i] = d * len( i );
if( d ) {
mx1[i] = rx1[i] = lx1[i] = sum[i];
mx0[i] = rx0[i] = lx0[i] = 0;
}
else {
mx1[i] = rx1[i] = lx1[i] = 0;
mx0[i] = rx0[i] = lx0[i] = len( i );
}
}//
void re( int i ) {//区间翻转
if( add[i] != -1 ) {
add[i] ^= 1;
ad( i, add[i] );
return;
}
rev[i] ^= 1;
sum[i] = len( i ) - sum[i];
int tem1 = mx1[i];
int tem2 = lx1[i];
int tem3 = rx1[i];
mx1[i] = mx0[i];
lx1[i] = lx0[i];
rx1[i] = rx0[i];
mx0[i] = tem1, lx0[i] = tem2, rx0[i] = tem3;
}
void Down( int i ) { //向下释放标记
if( add[i] != -1 ) {
ad( ls, add[i] ), ad( rs, add[i] );
add[ls] = add[rs] = add[i];
add[i] = -1;
return;
}
if( rev[i] ) {
re( ls ), re( rs );
rev[i] = 0;
}
}
void build( int l, int r, int i ) {
add[i] = -1, rev[i] = 0;
ll[i] = l, rr[i] = r;
if( l == r ) {
if( a[l] == 1 ) {
sum[i] = 1;
mx1[i] = lx1[i] = rx1[i] = 1;
lx0[i] = rx0[i] = mx0[i] = 0;
}
else {
sum[i] = 0;
mx1[i] = lx1[i] = rx1[i] = 0;
lx0[i] = rx0[i] = mx0[i] = 1;
}
return;
}
build( l, md, ls );
build( md + 1, r, rs );
Up( i );
}
void update( int l, int r, int v, int i ) {
if( ll[i] == l && rr[i] == r ) {
add[i] = v;
ad( i, v );
return;
}
Down( i );
if( r <= md )
update( l, r, v, ls );
else
if( l > md )
update( l, r, v, rs );
else
update( l, md, v, ls ), update( md + 1, r, v, rs );
Up( i );
}
void reve( int l, int r, int i ) {
if( ll[i] == l && rr[i] == r ) {
re( i );
return;
}
Down( i );
if( r <= md )
reve( l, r, ls );
else
if( l > md )
reve( l, r, rs );
else
reve( l, md, ls ), reve( md + 1, r, rs );
Up( i );
}
int qsum( int l, int r, int i ) {
if( ll[i] == l && rr[i] == r )
return sum[i];
Down( i );
if( r <= md )
return qsum( l, r, ls );
if( l > md )
return qsum( l, r, rs );
return qsum( l, md, ls ) + qsum( md + 1, r, rs );
}
int q1( int l, int r, int i ) { //查询连续1的个数的最大值
if( ll[i] == l && rr[i] == r )
return mx1[i];
Down( i );
if( r <= md )
return q1( l, r, ls );
if( l > md )
return q1( l, r, rs );
int ret = max( q1( l, md, ls ), q1( md + 1, r, rs ) );
if( rx1[ls] && lx1[rs] ) {//如果区间中点两边都是1的话最大值可能就是在区间的中间取得
int head = max( l, rr[ls] - rx1[ls] + 1 );
int tail = min( r, ll[rs] + lx1[rs] - 1 );
ret = max( ret, tail - head + 1 );
}
return ret;
}
void read() {
scanf( "%d%d", &n, &m );
for( int i = 1; i <= n; ++i )
scanf( "%d", &a[i] );
}
int main() {
// freopen( "tt.txt", "r", stdin );
int cas;
scanf( "%d", &cas );
while( cas-- ) {
read();
int op, l, r;
build( 1, n, 1 );
while( m-- ) {
scanf( "%d%d%d", &op, &l, &r );
l++, r++;
if( op == 0 ) {
update( l, r, 0, 1 );
}
if( op == 1 )
update( l, r, 1, 1 );
if( op == 2 )
reve( l, r, 1 );
if( op == 3 ) {
printf( "%d\n", qsum( l, r, 1 ) );
}
if( op == 4 )
printf( "%d\n", q1( l, r, 1 ) );
}
}
return 0;
}