Count Color
查询区间内颜色种类数,观察到颜色种类数只有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
主要考察对于区间多个性质的维护
维护最长连续空白区间,前缀最长,后缀最长,区间长度,以及区间下标左右端点
主要考察代码实现能力,思维量并不高
较简单
#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
因为 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™
引理:树上直径为一段连续区间去掉匹配括号的最大长度
显然,因为匹配的括号在树上就是对应的影响再次回到原点
括号匹配是线段树维护的一种模板,左括号为 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+∣b−c∣+d=max(a+b−c+d,a−b+c+d)
在形式上需要维护
- x = a + b \rm x=a+b x=a+b : 一个区间去掉匹配括号之后的后缀右括号加上后缀左括号的最大值
- y = a − b \rm y=a-b y=a−b : 一个区间去掉匹配括号之后的后缀右括号减去后缀左括号的最大值
- 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+∣b−r∣+l=max(a+b−r+l,a−b+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;a−b=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 b≥r : 右儿子不再有右括号,左儿子剩 b − r \rm b-r b−r个左括号 y = a − l − ( b − r ) \rm y=a-l-(b-r) y=a−l−(b−r)1.2
b < r \rm b<r b<r : 左儿子不再有左括号,右儿子剩 r − b \rm r-b r−b个右括号 y = ( r − b ) + a − l \rm y=(r-b)+a-l y=(r−b)+a−l
- 显然一个加大于 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 a−b=t[lson].y;l=t[rson].l;r=t[rson].r;y=r(−b+a)−l=r−l+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 l≥c 右儿子不再有右括号,左儿子剩 l − c \rm l-c l−c个左括号, y = d + ( l − c ) − r \rm y=d+(l-c)-r y=d+(l−c)−r -
1.2
l < c \rm l<c l<c 左儿子不再有左括号,右儿子剩 c − l \rm c-l c−l个右括号, y = d − ( c − l ) − r \rm y=d-(c-l)-r y=d−(c−l)−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+l−c−r=(−c+d)+l−r
-
-
-
考虑 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+∣l−c∣+r=max(d+l−c+r,d+c−l+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;
}