数据结构二之线段树Ⅰ——Count Color,Hotel,Transformation,Tree Generator™

本文详细介绍了线段树在处理区间查询和修改问题中的应用,包括颜色种类计数、区间性质维护和括号匹配问题。通过三个实例展示了线段树在不同场景下的实现和优化,如CountColor、Hotel和Transformation问题。每个例子都包含了代码实现,重点突出了如何根据题目需求灵活运用线段树维护区间信息。
摘要由CSDN通过智能技术生成

Count Color

POJ2777

查询区间内颜色种类数,观察到颜色种类数只有30,完全可以状压成整型存储,没有必要开30棵线段树

区间内有这颜色就置为 1 1 1,没有这个颜色就是 0 0 0

非常简单

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 100005
#define maxc 32
#define lson num << 1
#define rson num << 1 | 1
int n, T, m;
struct node {
	int tag, ans;
}t[maxn << 2];

void pushdown( int num ) {
	if( ! t[num].tag ) return;
	t[lson].ans = t[lson].tag = t[num].tag;
	t[rson].ans = t[rson].tag = t[num].tag;
	t[num].tag = 0;
}

void modify( int num, int l, int r, int L, int R, int x ) {
	if( r < L or R < l ) return;
	if( L <= l and r <= R ) {
		t[num].ans = t[num].tag = 1 << x;
		return;
	}
	pushdown( num );
	int mid = ( l + r ) >> 1;
	modify( lson, l, mid, L, R, x );
	modify( rson, mid + 1, r, L, R, x );
	t[num].ans = t[lson].ans | t[rson].ans;
}

void build( int num, int l, int r ) {
	t[num].ans = 1 << 1, t[num].tag = 0;
	if( l == r ) return;
	int mid = ( l + r ) >> 1;
	build( lson, l, mid );
	build( rson, mid + 1, r );
}

int query( int num, int l, int r, int L, int R ) {
	if( R < l or r < L ) return 0;
	if( L <= l and r <= R ) return t[num].ans;
	pushdown( num );
	int mid = ( l + r ) >> 1;
	return query( lson, l, mid, L, R ) | query( rson, mid + 1, r, L, R );
}

int main() {
	scanf( "%d %d %d", &n, &T, &m );
	build( 1, 1, n );
	char opt[5]; int l, r, x;
	while( m -- ) {
		scanf( "%s %d %d", opt, &l, &r );
		if( l > r ) swap( l, r );
		if( opt[0] == 'C' ) {
			scanf( "%d", &x );
			modify( 1, 1, n, l, r, x );
		}
		else {
			int ans = query( 1, 1, n, l, r );
			printf( "%d\n", __builtin_popcount( ans ) );			
		}
	}
	return 0;
}

Hotel

POS3667

主要考察对于区间多个性质的维护

维护最长连续空白区间,前缀最长,后缀最长,区间长度,以及区间下标左右端点

主要考察代码实现能力,思维量并不高

较简单

#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 50005
#define lson num << 1
#define rson num << 1 | 1
struct node {
	int l, r, len, Max, lmax, rmax, tag;
/*
len 区间长度
Max 最长的连续空白区间
lmax 区间左端开始向右连续的空白区间长度
rmax 区间右端开始向左连续的空白区间长度 
*/ 
}t[maxn << 2];
int n, m;

void build( int num, int l, int r ) {
	t[num].len = t[num].Max = t[num].lmax = t[num].rmax = r - l + 1;
	t[num].l = l, t[num].r = r, t[num].tag = 0;
	if( l == r ) return;
	int mid = l + r >> 1;
	build( lson, l, mid );
	build( rson, mid + 1, r );
}

void pushup( int num ) {
	if( t[lson].lmax == t[lson].len ) t[num].lmax = t[lson].len + t[rson].lmax;
	else t[num].lmax = t[lson].lmax;
	if( t[rson].rmax == t[rson].len ) t[num].rmax = t[rson].len + t[lson].rmax;
	else t[num].rmax = t[rson].rmax;
	t[num].Max = max( t[lson].rmax + t[rson].lmax, max( t[lson].Max, t[rson].Max ) );
}

void pushdown( int num ) {
	if( ! t[num].tag ) return;
	else if( ~ t[num].tag ) {
		t[lson].lmax = t[lson].rmax = t[lson].Max = 0;
		t[rson].lmax = t[rson].rmax = t[rson].Max = 0;
	}
	else {
		t[lson].lmax = t[lson].rmax = t[lson].Max = t[lson].len;
		t[rson].lmax = t[rson].rmax = t[rson].Max = t[rson].len;
	}
	t[lson].tag = t[rson].tag = t[num].tag;
	t[num].tag = 0;
}

void modify( int num, int l, int r, int L, int R, int k ) {
	if( R < l || r < L ) return;
	if( L <= l && r <= R ) {
		if( ~ k ) t[num].lmax = t[num].rmax = t[num].Max = 0;
		else t[num].lmax = t[num].rmax = t[num].Max = t[num].len;
		t[num].tag = k;
		return;
	}
	pushdown( num );
	int mid = ( l + r ) >> 1;
	modify( lson, l, mid, L, R, k );
	modify( rson, mid + 1, r, L, R, k );
	pushup( num );
}

int query( int num, int l, int r, int k ) {
	pushdown( num );
	if( t[num].l == t[num].r ) return l;
	int mid = t[num].l + t[num].r >> 1;
	if( t[lson].Max >= k ) return query( lson, l, r, k );
	else if( t[rson].lmax + t[lson].rmax >= k ) return mid - t[lson].rmax + 1;
	else return query( rson, mid + 1, r, k );
}

int main() {
	scanf( "%d %d", &n, &m );
	build( 1, 1, n );
	int opt, x, d, pos;
	while( m -- ) {
		scanf( "%d", &opt );
		if( opt & 1 ) {
			scanf( "%d", &d );
			if( t[1].Max < d ) printf( "0\n" );
			else {
				printf( "%d\n", pos = query( 1, 1, n, d ) );
				modify( 1, 1, n, pos, pos + d - 1, 1 );
			}
		}
		else {
			scanf( "%d %d", &x, &d );
			modify( 1, 1, n, x, x + d - 1, -1 );
		}
	}
	return 0;
}

Transformation

HDU4578

因为 p ∈ [ 1 , 3 ] p\in [1,3] p[1,3],所以直接线段树大暴力维护数不同次方的和

  • tag : 赋值标记
  • add : 加标记
  • mul : 乘标记
  • sum1 : 区间内数的一次方和
  • sum2 : 区间内数的二次方和
  • sum3 : 区间内数的三次方和

标记内有高低优先顺序,赋值第一,乘法第二,加法第三
( a + x ) 2 = a 2 + x 2 + 2 a x (a+x)^2=a^2+x^2+2ax (a+x)2=a2+x2+2ax

( a 1 + x ) 2 + ( a 2 + x ) 2 + . . . + ( a n + x ) 2 = ( a 1 2 + a 2 2 + . . . + a n 2 ) + 2 x ( a 1 + a 2 + . . . + a n ) + n x 2 (a_1+x)^2+(a_2+x)^2+...+(a_n+x)^2=(a_1^2+a_2^2+...+a_n^2)+2x(a_1+a_2+...+a_n)+nx^2 (a1+x)2+(a2+x)2+...+(an+x)2=(a12+a22+...+an2)+2x(a1+a2+...+an)+nx2

( a + x ) 3 = a 3 + 3 a 2 x + 3 a x 2 + x 3 (a+x)^3=a^3+3a^2x+3ax^2+x^3 (a+x)3=a3+3a2x+3ax2+x3

( a 1 + x ) 3 + . . . + ( a n + x ) 3 = ( a 1 3 + . . . + a n 3 ) + 3 x ( a 1 2 + . . . + a n 2 ) + 3 x 2 ( a 1 + . . . + a n ) + n x 3 (a_1+x)^3+...+(a_n+x)^3=(a_1^3+...+a_n^3)+3x(a_1^2+...+a_n^2)+3x^2(a_1+...+a_n)+nx^3 (a1+x)3+...+(an+x)3=(a13+...+an3)+3x(a12+...+an2)+3x2(a1+...+an)+nx3

通过公示可以看出,计算加法也有优先顺序,三次方第一,二次方第二,一次方第三

主要考察维护性质标记间的优先顺序,对代码实现能力要求更高,思维含量不大,中档

#include <cstdio>
#define mod 10007
#define maxn 100005
#define int long long
#define lson num << 1
#define rson num << 1 | 1
struct node {
	int l, r, sum1, sum2, sum3, add, mul, tag;
	void update1( int x ) {
		add = ( add + x ) % mod;
		sum3 = ( sum3 + 3 * x * sum2 + 3 * x * x * sum1 + ( r - l + 1 ) * x * x * x ) % mod;
		sum2 = ( sum2 + 2 * x * sum1 + ( r - l + 1 ) * x * x ) % mod;
		sum1 = ( sum1 + x * ( r - l + 1 ) ) % mod;
	}
	void update2( int x ) {
		add = add * x % mod;
		mul = mul * x % mod;
		sum3 = sum3 * x * x * x % mod;
		sum2 = sum2 * x * x % mod;
		sum1 = sum1 * x % mod;
	}
	void update3( int x ) {
		tag = x;
		mul = 1;
		add = 0;
		sum3 = x * x * x * ( r - l + 1 ) % mod;
		sum2 = x * x * ( r - l + 1 ) % mod;
		sum1 = x * ( r - l + 1 ) % mod;
	}
}t[maxn << 2];

node operator + ( node x, node y ) {
	node ans;
	ans.l = x.l, ans.r = y.r;
	ans.tag = ans.add = 0, ans.mul = 1;
	ans.sum1 = ( x.sum1 + y.sum1 ) % mod;
	ans.sum2 = ( x.sum2 + y.sum2 ) % mod;
	ans.sum3 = ( x.sum3 + y.sum3 ) % mod;
	return ans;
}

void build( int num, int l, int r ) {
	t[num].sum1 = t[num].sum2 = t[num].sum3 = 0;
	t[num].add = t[num].tag = 0, t[num].mul = 1;
	t[num].l = l, t[num].r = r;
	if( l == r ) return;
	int mid = l + r >> 1;
	build( lson, l, mid );
	build( rson, mid + 1, r );
}

void pushdown( int num, int l, int r ) {
	if( t[num].tag ) {
		t[lson].update3( t[num].tag );
		t[rson].update3( t[num].tag );
	}
	if( t[num].mul ^ 1 ) {
		t[lson].update2( t[num].mul );
		t[rson].update2( t[num].mul );
	}
	if( t[num].add ) {
		t[lson].update1( t[num].add );
		t[rson].update1( t[num].add );
	}
	t[num].tag = t[num].add = 0, t[num].mul = 1;
}

void modify1( int num, int l, int r, int L, int R, int x ) {
	if( r < L || R < l ) return;
	if( L <= l && r <= R ) { t[num].update1( x ); return; }
	pushdown( num, l, r );
	int mid = l + r >> 1;
	modify1( lson, l, mid, L, R, x );
	modify1( rson, mid + 1, r, L, R, x );
	t[num] = t[lson] + t[rson];
}

void modify2( int num, int l, int r, int L, int R, int x ) {
	if( r < L || R < l ) return;
	if( L <= l && r <= R ) { t[num].update2( x ); return; }
	pushdown( num, l, r );
	int mid = l + r >> 1;
	modify2( lson, l, mid, L, R, x );
	modify2( rson, mid + 1, r, L, R, x );
	t[num] = t[lson] + t[rson];
}

void modify3( int num, int l, int r, int L, int R, int x ) {
	if( r < L || R < l ) return;
	if( L <= l && r <= R ) { t[num].update3( x ); return; }
	pushdown( num, l, r );
	int mid = l + r >> 1;
	modify3( lson, l, mid, L, R, x );
	modify3( rson, mid + 1, r, L, R, x );
	t[num] = t[lson] + t[rson];
}

node query( int num, int l, int r, int L, int R ) {
	if( L <= l && r <= R ) return t[num];
	pushdown( num, l, r );
	int mid = l + r >> 1;
	if( R <= mid ) return query( lson, l, mid, L, R );
	else if( mid < L ) return query( rson, mid + 1, r, L, R );
	else return query( lson, l, mid, L, R ) + query( rson, mid + 1, r, L, R );
}

signed main() {
	int n, m, opt, l, r, c;
	while( scanf( "%lld %lld", &n, &m ) and n and m ) {
		build( 1, 1, n );
		while( m -- ) {
			scanf( "%lld %lld %lld %lld", &opt, &l, &r, &c );
			switch ( opt ) {
				case 1 : { modify1( 1, 1, n, l, r, c ); break; }
				case 2 : { modify2( 1, 1, n, l, r, c ); break; }
				case 3 : { modify3( 1, 1, n, l, r, c ); break; }
				case 4 : { 
					node ans = query( 1, 1, n, l, r );
					switch ( c ) {
						case 1 : { printf( "%lld\n", ans.sum1 ); break; }
						case 2 : { printf( "%lld\n", ans.sum2 ); break; }
						case 3 : { printf( "%lld\n", ans.sum3 ); break; }
					}
					break;
				}
			}
		}
	}
	return 0;
}

Tree Generator™

CF1149C

引理:树上直径为一段连续区间去掉匹配括号的最大长度

显然,因为匹配的括号在树上就是对应的影响再次回到原点

括号匹配是线段树维护的一种模板,左括号为 1 1 1,右括号为 − 1 -1 1

显然将匹配括号去掉后,区间形如))))((((可以只有()

考虑合并答案

假设这个区间的左子区间有右括号 a a a个,左括号 b b b个,右子区间有右括号c个,左括号b个

那么这个区间合并的答案就是 a + ∣ b − c ∣ + d = m a x ( a + b − c + d , a − b + c + d ) \rm a+|b-c|+d=max(a+b-c+d,a-b+c+d) a+bc+d=max(a+bc+d,ab+c+d)

在形式上需要维护

  • x = a + b \rm x=a+b x=a+b : 一个区间去掉匹配括号之后的后缀右括号加上后缀左括号的最大值
  • y = a − b \rm y=a-b y=ab : 一个区间去掉匹配括号之后的后缀右括号减去后缀左括号的最大值
  • z = − c + d \rm z=-c+d z=c+d : 一个区间去掉匹配括号之后的前缀左括号减去前缀右括号的最大值
  • w = c + d \rm w=c+d w=c+d : 一个区间去掉匹配括号之后的前缀左括号加上前缀右括号的最大值

前缀/后缀指从区间左端/右端开始的连续一段

由下往上儿子更新父亲

  • 考虑维护 x x x

    • 最优划分点在右边
      • 直接右儿子 t [ r s o n ] . x \rm t[rson].x t[rson].x更新
    • 最优划分点在左边
      • 则一定包含右儿子区间,形如)))((
      • 假设在左儿子的最佳划分点后面 a a a个右括号, b b b个左括号
      • 右儿子剩下 l l l个左括号, r r r个右括号
      • 左右儿子括号匹配后
        • x = a + ∣ b − r ∣ + l = m a x ( a + b − r + l , a − b + r + l ) \rm x=a+|b-r|+l=max(a+b-r+l,a-b+r+l) x=a+br+l=max(a+br+l,ab+r+l)
      • 同时 a + b = t [ l s o n ] . x ; a − b = t [ l s o n ] . y ; l = t [ r s o n ] . l ; r = t [ r s o n ] . r \rm a+b=t[lson].x;a-b=t[lson].y;l=t[rson].l;r=t[rson].r a+b=t[lson].x;ab=t[lson].y;l=t[rson].l;r=t[rson].r
  • 考虑维护 y y y

    • 最优划分点在右边

      • 直接右儿子 t [ r s o n ] . y \rm t[rson].y t[rson].y更新
    • 最优划分点在左边

      • 则一定包含右儿子区间
      • 假设在左儿子的最佳划分点后面 a a a个右括号, b b b个左括号
      • 右儿子剩下 l l l个左括号, r r r个右括号
      • 左右儿子括号匹配后
        • 1.1 b ≥ r \rm b\ge r br : 右儿子不再有右括号,左儿子剩 b − r \rm b-r br个左括号 y = a − l − ( b − r ) \rm y=a-l-(b-r) y=al(br)
        • 1.2 b < r \rm b<r b<r : 左儿子不再有左括号,右儿子剩 r − b \rm r-b rb个右括号 y = ( r − b ) + a − l \rm y=(r-b)+a-l y=(rb)+al
      • 显然一个加大于 0 0 0的数,一个减大于 0 0 0的数
      • m a x \rm max max选择1.2更新,不用像 x x x一样分情况
      • 同时 a − b = t [ l s o n ] . y ; l = t [ r s o n ] . l ; r = t [ r s o n ] . r ; y = r ( − b + a ) − l = r − l + t [ l s o n ] . y \rm a-b=t[lson].y;l=t[rson].l;r=t[rson].r;y=r(-b+a)-l=r-l+t[lson].y ab=t[lson].y;l=t[rson].l;r=t[rson].r;y=r(b+a)l=rl+t[lson].y
  • 考虑 z z z

    • 最优划分点在左边

      • 直接左儿子 t [ l s o n ] . z \rm t[lson].z t[lson].z更新
    • 最优划分点在右边

      • 则一定包含左儿子区间

      • 假设在右儿子的最佳划分点前面 c c c个右括号, d d d个左括号

      • 左儿子剩下 l l l个左括号 r r r个右括号

      • 左右儿子括号匹配

        • 1.1 l ≥ c \rm l\ge c lc 右儿子不再有右括号,左儿子剩 l − c \rm l-c lc个左括号, y = d + ( l − c ) − r \rm y=d+(l-c)-r y=d+(lc)r

        • 1.2 l < c \rm l<c l<c 左儿子不再有左括号,右儿子剩 c − l \rm c-l cl个右括号, y = d − ( c − l ) − r \rm y=d-(c-l)-r y=d(cl)r

      • 显然一个加大于 0 0 0的数,一个减大于 0 0 0的数

      • m a x \rm max max选择1.1更新

      • 同时 − c + d = t [ r s o n ] . z l = t [ l s o n ] . l r = t [ l s o n ] . r z = d + l − c − r = ( − c + d ) + l − r \rm -c+d=t[rson].z l=t[lson].l r=t[lson].rz=d+l-c-r=(-c+d)+l-r c+d=t[rson].zl=t[lson].lr=t[lson].rz=d+lcr=(c+d)+lr

  • 考虑 k k k

    • 最优划分点在左边
      • 直接左儿子 t [ l s o n ] . w \rm t[lson].w t[lson].w更新
    • 最优划分点在右边
      • 则一定包含左儿子区间
      • 假设在右儿子的最佳划分点前面 c c c个右括号, d d d个左括号
      • 左儿子剩下 l l l个左括号, r r r个右括号
      • 左右儿子括号匹配后
        • x = d + ∣ l − c ∣ + r = m a x ( d + l − c + r , d + c − l + r ) \rm x=d+|l-c|+r=max(d+l-c+r,d+c-l+r) x=d+lc+r=max(d+lc+r,d+cl+r)
      • 同时 − c + d = t [ r s o n ] . z c + d = t [ r s o n ] . w l = t [ l s o n ] . l r = t [ l s o n ] . r \rm -c+d=t[rson].z c+d=t[rson].w l=t[lson].l r=t[lson].r c+d=t[rson].zc+d=t[rson].wl=t[lson].lr=t[lson].r

困难

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 300005
#define lson num << 1
#define rson num << 1 | 1
struct node {
	//全是取最大值
	//全是区间内去掉匹配括号后 形如)))))(( 
	int x;	//后缀左括号加后缀右括号 
	int y;	//后缀右括号减后缀左括号 
	int z;	//前缀左括号减前缀右括号 
	int w;	//前缀左括号加前缀右括号 
	int l;	//区间内未匹配左括号数量 
	int r;	//区间内未匹配有括号数量 
	int ans;//区间内的最长直径答案 
	//前缀/后缀指从区间左端/右端开始的连续一段s[i] 
}t[maxn << 2];
int n, m;
char s[maxn];

void pushup( int num ) {
	//左右儿子区间会有左儿子的左括号和右儿子的有括号匹配情况 
	if( t[lson].l > t[rson].r ) {
		t[num].l = t[lson].l - t[rson].r + t[rson].l;
		t[num].r = t[lson].r;
	}
	else {
		t[num].l = t[rson].l;
		t[num].r = t[lson].r + t[rson].r - t[lson].l;
	}
	/*
	后缀左括号加后缀右括号 
	1.最优划分点在右边    直接右儿子t[rson].x更新 
	2.最优划分点在左边  则一定包含右儿子区间 形如)))(( 
	  假设在左边儿子的最佳划分点后面有a个右括号b个左括号 
	  右边儿子剩下l个左括号r个右括号 
	  左右儿子括号匹配后
	  x=a+|b-r|+l=max(a+b-r+l,a-b+r+l)
	  同时已知有 a+b=t[lson].x a-b=t[lson].y l=t[rson].l r=t[rson].r
	*/
	t[num].x = max( t[rson].x, max( t[lson].x - t[rson].r + t[rson].l, t[lson].y + t[rson].r + t[rson].l ) );
	/*
	后缀右括号减后缀左括号
	1.最优划分点在右边    直接右儿子t[rson].y更新 
	2.最优划分点在左边  则一定包含右儿子区间
	  假设在左边儿子的最佳划分点后面有a个右括号b个左括号 
	  右边儿子剩下l个左括号r个右括号 
	  左右儿子括号匹配
	  1.1 b>=r  右儿子不再有右括号 左儿子剩b-r个左括号   y=a-l-(b-r)
	  1.2 b<r   左儿子不再有左括号 右儿子剩r-b个右括号   y=(r-b)+a-l
	  显然一个加大于0的数 一个减大于0的数
	  max选择1.2更新 不用像x一样分情况 
	  同时已知有 a-b=t[lson].y l=t[rson].l r=t[rson].r
	  y=r(-b+a)-l=r-l+t[lson].y
	*/
	t[num].y = max( t[rson].y, t[lson].y + t[rson].r - t[rson].l );
	/*
	前缀左括号减前缀右括号 
	1.最优划分点在左边    直接左儿子t[lson].z更新 
	2.最优划分点在右边  则一定包含左儿子区间
	  假设在右边儿子的最佳划分点前面有c个右括号d个左括号 
	  左边儿子剩下l个左括号r个右括号 
	  左右儿子括号匹配
	  1.1 l>=c  右儿子不再有右括号 左儿子剩l-c个左括号   y=d+(l-c)-r
	  1.2 l<c   左儿子不再有左括号 右儿子剩c-l个右括号   y=d-(c-l)-r
	  显然一个加大于0的数 一个减大于0的数
	  max选择1.1更新 同时已知有-c+d=t[rson].z l=t[lson].l r=t[lson].r
	  z=d+l-c-r=(-c+d)+l-r
	*/
	t[num].z = max( t[lson].z, t[rson].z + t[lson].l - t[lson].r );
	/*
	前缀左括号加前缀右括号
	1.最优划分点在左边    直接左儿子t[lson].w更新 
	2.最优划分点在右边    则一定包含左儿子区间 
	  假设在右儿子的最佳划分点前面有c个右括号d个左括号 
	  左儿子剩下l个左括号r个右括号 
	  左右儿子括号匹配后
	  x=d+|l-c|+r=max(d+l-c+r,d+c-l+r)
	  同时已知有 -c+d=t[rson].z c+d=t[rson].w l=t[lson].l r=t[lson].r
	*/
	t[num].w = max( t[lson].w, max( t[lson].l + t[lson].r + t[rson].z, t[lson].r - t[lson].l + t[rson].w ) );
	/*
	合并答案 
	1.就是左儿子或右儿子的答案
	2.假设这个区间的左子区间有右括号a个左括号b个 右子区间有右括号c个左括号b个
	  那么这个区间从中间合并的答案就是a+|b-c|+d=max(a+b-c+d,a-b+c+d)
	  同时已知有a+b=t[lson].x a-b=t[lson].y -c+d=t[rson].z c+d=t[rson]w 
	*/
	t[num].ans = max( max( t[lson].ans, t[rson].ans ), max( t[lson].x + t[rson].z, t[lson].y + t[rson].w ) );
}

void modify( int num, int k ) {
	t[num].x = 1;
	t[num].y = max( -k, 0 );
	t[num].z = max( k, 0 );
	t[num].l = k == 1;
	t[num].r = k == -1;
	t[num].w = 1;
	t[num].ans = 1;
}

void build( int num, int l, int r ) {
	if( l == r ) { modify( num, s[l] == '(' ? 1 : -1 ); return; }
	int mid = l + r >> 1;
	build( lson, l, mid );
	build( rson, mid + 1, r );
	pushup( num );
}

void modify( int num, int l, int r, int pos, int k ) {
	if( l == r ) { modify( num, k ); return; }
	int mid = l + r >> 1;
	if( pos <= mid ) modify( lson, l, mid, pos, k );
	else modify( rson, mid + 1, r, pos, k );
	pushup( num );
}

int main() {
	scanf( "%d %d %s", &n, &m, s + 1 );
	n = strlen( s + 1 );
	build( 1, 1, n );
	printf( "%d\n", t[1].ans );
	while( m -- ) {
		int x, y;
		scanf( "%d %d", &x, &y );
		modify( 1, 1, n, x, s[y] == '(' ? 1 : -1 );
		modify( 1, 1, n, y, s[x] == '(' ? 1 : -1 );
		swap( s[x], s[y] );
		printf( "%d\n", t[1].ans );
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值