JZOJ2413. 【NOI2005】维护数列
2016-04-13 11:57
381 查看
题目大意
维护一个数列,支持以下操作:插入 INSERT_posi_tot_c1_c2_…_ctot 在当前数列的第posi个数字后插入tot个数字:c1, c2, …, ctot;若在数列首插入,则posi为0
删除 DELETE_posi_tot 从当前数列的第posi个数字开始连续删除tot个数字
修改 MAKE-SAME_posi_tot_c 将当前数列的第posi个数字开始的连续tot个数字统一修改为c
翻转 REVERSE_posi_tot 取出从当前数列的第posi个数字开始的tot个数字,翻转后放入原来的位置
求和 GET-SUM_posi_tot 计算从当前数列开始的第posi个数字开始的tot个数字的和并输出
求和最大的子列 MAX-SUM 求出当前数列中和最大的一段子列,并输出最大和
Data Constraint
你可以认为在任何时刻,数列中至少有1个数。
输入数据一定是正确的,即指定位置的数在数列中一定存在。
50%的数据中,任何时刻数列中最多含有30 000个数;
100%的数据中,任何时刻数列中最多含有500 000个数。
100%的数据中,任何时刻数列中任何一个数字均在[-1 000, 1 000]内。
100%的数据中,M ≤20 000,插入的数字总数不超过4 000 000个,输入文件大小不超过20MBytes。
题解
这么直白的题面,显然就是数据结构。又看到有翻转操作,那基本就可以确定是Splay了。具体而言,Splay维护以下信息(以u为根节点的子树):
子树大小Size[u]
赋值标记SAME[u]
翻转标记REV[u]
子树所有值之和Sum[u]
子树最大子序列之和MAXSUM[u]
区间最大前缀和Lsum[u]
区间最大后缀和Rsum[u]
易得
MAXSUM[u]=Max(MAXSUM[L],MAXSUM[R],Rsum[L]+Val[u]+Lsum[R])
再维护以下操作
GetRange( l , r )
将l旋到根,r旋到l的右儿子。此时r的右儿子就是区间[l+1,r−1]void GetRange( int l , int r ) { int x = Get(l) ; int y = Get(r) ; Splay( x , 0 ) ; Splay( y , Root ) ; }
Insert( pos , num )
在第pos个数后插入num个数。先将pos旋到根,再将pos+1旋到右儿子,即GetRange( pos + 1 , pos + 2 )(+1是因为之前在首尾各加了一个节点,下同)
void Insert( int pos , int num ) { GetRange( pos + 1 , pos + 2 ) ; Son[Son[Root][1]][0] = Build( 1 , num , Son[Root][1] ) ; Update( Son[Root][1] ) ; Update( Root ) ; }
Delete( l , r )
删除区间[l,r]还是先旋根取区间,再直接删除即可。
这题会卡空间,所以删除的时候要将删除的节点编号放入一个队列里以备下一次插入节点时使用,即GetID
void Delete( int l , int r ) { GetRange( l , r + 2 ) ; int R = Son[Root][1] ; GetID( Son[R][0] ) ; Pre[Son[R][0]] = 0 ; Son[R][0] = 0 ; Update( R ) ; Update( Root ) ; }
MAKE_SAME( l , r , c )
将区间[l,r]全部修改为c直接在SAME上打标记。
void MAKE_SAME( int l , int r , int c ) { GetRange( l , r + 2 ) ; int L = Son[Son[Root][1]][0] ; SAME[L] = c ; Push( Son[Root][1] ) ; Update( Son[Root][1] ) ; Update( Root ) ; }
Reverse( l , r )
翻转区间[l,r]直接打标记。
void Reverse( int l , int r ) { GetRange( l , r + 2 ) ; int L = Son[Son[Root][1]][0] ; REV[L] ^= 1 ; Push( Son[Root][1] ) ; Update( Son[Root][1] ) ; Update( Root ) ; }
GET_SUM( l , r )
求区间[l,r]的和。直接返回信息。
int GET_SUM( int l , int r ) { GetRange( l , r + 2 ) ; int L = Son[Son[Root][1]][0] ; Push( L ) ; return Sum[L] ; }
从这题也可以看出Splay在诸如区间修改,区间翻转等操作上的优势。且整体代码还是比较直观易懂的。
需要注意的是,由于大量运用懒标记,一定要及时更新标记!!!
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 2000000 + 10
bool REV
;
int MAXSUM
, Lsum
, Rsum
, SAME
;
int Pre
, Son
[2] , Sum
, Size
, Val
;
int A
, D
, S
;
char op[20] ;
int n , m , Root , tot , top ;
void Push( int v ) {
if ( !v ) return ;
int L = Son[v][0] ;
int R = Son[v][1] ;
if ( REV[v] ) {
REV[v] ^= 1 ;
REV[L] ^= 1 ;
REV[R] ^= 1 ;
swap( Son[v][0] , Son[v][1] ) ;
swap( Lsum[v] , Rsum[v] ) ;
}
if ( SAME[v] <= 10000 ) {
Val[v] = SAME[v] ;
SAME[L] = SAME[R] = SAME[v] ;
Sum[v] = SAME[v] * Size[v] ;
MAXSUM[v] = max( SAME[v] , Sum[v] ) ;
Lsum[v] = Rsum[v] = max( 0 , Sum[v] ) ;
SAME[v] = 1e6 ;
}
}
void Update( int now ) {
int L = Son[now][0] ;
int R = Son[now][1] ;
Push( L ) , Push( R ) ;
Size[now] = Size[L] + Size[R] + 1 ;
Sum[now] = Sum[L] + Sum[R] + Val[now] ;
Lsum[now] = max( Lsum[L] , Sum[L] + Val[now] + Lsum[R] ) ;
Rsum[now] = max( Rsum[R] , Sum[R] + Val[now] + Rsum[L] ) ;
MAXSUM[now] = max( max( MAXSUM[L] , MAXSUM[R] ) , Rsum[L] + Val[now] + Lsum[R] ) ;
}
int Build( int l , int r , int F ) {
if ( l > r ) return 0 ;
int mid = (l + r) / 2 ;
int now = top ? S[top --] : ++ tot ;
REV[now] = 0 ;
Pre[now] = F ;
SAME[now] = 1e6 ;
Val[now] = Sum[now] = A[mid] ;
Son[now][0] = Build( l , mid - 1 , now ) ;
Son[now][1] = Build( mid + 1 , r , now ) ;
Update( now ) ;
return now ;
}
void Rotate( int x ) {
int F = Pre[x] , G = Pre[F] ;
int Side = ( Son[F][1] == x ) ;
Pre[x] = G , Pre[F] = x ;
Son[F][Side] = Son[x][!Side] ;
Pre[Son[F][Side]] = F ;
Son[G][Son[G][1]==F] = x ;
Son[x][!Side] = F ;
Update( F ) ;
Update( x ) ;
}
void Splay( int x , int Goal ) {
D[0] = 0 ;
for (int Now = x ; Now ; Now = Pre[Now] ) D[++D[0]] = Now ;
for (int i = D[0] ; i ; i -- ) Push( D[i] ) ;
while ( Pre[x] != Goal ) {
int y = Pre[x] , z = Pre[y] ;
if ( z != Goal ) {
if ( ( Son[y][0] == x ) ^ ( Son[z][0] == y ) ) Rotate( x ) ;
else Rotate( y ) ;
}
Rotate( x ) ;
}
Update( x ) ;
if ( !Goal ) Root = x ;
}
int Get( int x ) {
for (int Now = Root ; x ; ) {
Push( Now ) ;
if ( x == Size[Son[Now][0]] + 1 ) return Now ;
if ( x < Size[Son[Now][0]] + 1 ) Now = Son[Now][0] ;
else x -= Size[Son[Now][0]] + 1 , Now = Son[Now][1] ;
}
return 0 ;
}
void GetRange( int l , int r ) { int x = Get(l) ; int y = Get(r) ; Splay( x , 0 ) ; Splay( y , Root ) ; }
void Insert( int pos , int num ) { GetRange( pos + 1 , pos + 2 ) ; Son[Son[Root][1]][0] = Build( 1 , num , Son[Root][1] ) ; Update( Son[Root][1] ) ; Update( Root ) ; }
void GetID( int x ) {
if ( !x ) return ;
S[++top] = x ;
GetID( Son[x][0] ) ;
GetID( Son[x][1] ) ;
}
void Delete( int l , int r ) { GetRange( l , r + 2 ) ; int R = Son[Root][1] ; GetID( Son[R][0] ) ; Pre[Son[R][0]] = 0 ; Son[R][0] = 0 ; Update( R ) ; Update( Root ) ; }
void MAKE_SAME( int l , int r , int c ) { GetRange( l , r + 2 ) ; int L = Son[Son[Root][1]][0] ; SAME[L] = c ; Push( Son[Root][1] ) ; Update( Son[Root][1] ) ; Update( Root ) ; }
void Reverse( int l , int r ) { GetRange( l , r + 2 ) ; int L = Son[Son[Root][1]][0] ; REV[L] ^= 1 ; Push( Son[Root][1] ) ; Update( Son[Root][1] ) ; Update( Root ) ; }
int GET_SUM( int l , int r ) { GetRange( l , r + 2 ) ; int L = Son[Son[Root][1]][0] ; Push( L ) ; return Sum[L] ; }
int main() {
scanf( "%d%d" , &n , &m ) ;
for (int i = 2 ; i <= n + 1 ; i ++ ) scanf( "%d" , &A[i] ) ;
MAXSUM[0] = A[1] = A[n+2] = -1e6 ;
Root = Build( 1 , n + 2 , 0 ) ;
for (int i = 1 ; i <= m ; i ++ ) {
scanf( "%s" , op + 1 ) ;
if ( op[1] == 'I' ) {
int pos , num ;
scanf( "%d%d" , &pos , &num ) ;
for (int i = 1 ; i <= num ; i ++ ) scanf( "%d" , &A[i] ) ;
Insert( pos , num ) ;
}
if ( op[1] == 'D' ) {
int pos , num ;
scanf( "%d%d" , &pos , &num ) ;
Delete( pos , pos + num - 1 ) ;
}
if ( op[1] == 'M' ) {
if ( op[3] == 'K' ) {
int pos , num , c ;
scanf( "%d%d%d" , &pos , &num , &c ) ;
MAKE_SAME( pos , pos + num - 1 , c ) ;
} else printf( "%d\n" , MAXSUM[Root] ) ;
}
if ( op[1] == 'R' ) {
int pos , num ;
scanf( "%d%d" , &pos , &num ) ;
Reverse( pos , pos + num - 1 ) ;
}
if ( op[1] == 'G' ) {
int pos , num ;
scanf( "%d%d" , &pos , &num ) ;
printf( "%d\n" , GET_SUM( pos , pos + num - 1 ) ) ;
}
}
return 0 ;
}
以上.
相关文章推荐
- nchan-nginx
- centos查看设置端口开放状态
- win8.1 opnet14.5 一段时间后不用license老是失效解决方法
- docker 私有仓库 registry 部署
- Powershell实例小结(服务管理)
- Nginx下流量拦截算法 | 夏日小草
- MAC下配置ZSH
- Centos7.2中DNS服务named无法启动问题解决方案
- java.lang.UnsatisfiedLinkError: D:\Tomcat\apache-tomcat-7.0.37\bin\tcnative-1.dll: Can't load AMD 6
- docker 私有仓库配置
- Linux ps命令
- linux信号量解析
- Nginx在Linux和windows下的安装使用
- opencv之边界扩展copyMakeBorder
- 格式化sd 卡linux 脚本学习
- boost::scoped_ptr用法
- 【ssh-copy-id】
- 弹出popwindow 背景变暗
- Hadoop 基本操作
- 如何计算IOPS ?