【SPOJ】1557 Can you answer these queries II 线段树

传送门:【SPOJ】1557 Can you answer these queries II


题目分析:区间最大连续子段和,且子段内的相同数只计算一次。

每个数只算一次迫使我们使用离线算法:将询问离线,按照右端点排好序,然后从左到右插入每个数,每个数记录之前出现的位置,每次插入都在之前位置+1到当前位置,目的是确保每个数只计算一次。然后遇到右端点就进行一次查询。

我们用区间更新的方法,使得每个节点保存的是一段区间的值而不是一个变量。

每次更新区间【pre[ num[ i ] ] + 1, i】,pre[ num[ i ] ]为上一次num[ i ]出现的位置,区间内所有节点值+num[ i ]。

按照从左到右的顺序更新过去,每个叶子节点所在的位置L保存的是【L,R】内的元素和(R为更新到的位置)。

然后除了保存【L,R】内的元素和,我们还要保存以L为起点的最大子段和。

设置两个lazy标记,add为每次必须加标记,maxadd为前一种所有的标记里面取的最大值。

设置两个sum,now为从【L,R】内的元素和,old为前一种sum中的最大值。

pushdown的时候需要多多注意,注意一下更新的逻辑顺序就好。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#define travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define FOR( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , m
#define rson rs , m + 1 , r
#define rt o , l , r
#define root 1 , 1 , n
#define mid ( ( l + r ) >> 1 )

const int MAXN = 100005 ;
const int MAXE = 100005 ;

struct Edge {
	int L , idx ;
	Edge* next ;
} E[MAXE] , *H[MAXN] , *edge ;

int maxadd[MAXN << 2] ;
int old[MAXN << 2] ;
int now[MAXN << 2] ;
int add[MAXN << 2] ;
int pre[MAXN << 2] ;
int num[MAXN] ;
int ans[MAXN] ;
int n , q ;

void clear () {
	edge = E ;
	clr ( H , 0 ) ;
	clr ( add , 0 ) ;
	clr ( old , 0 ) ;
	clr ( now , 0 ) ;
	clr ( pre , 0 ) ;
	clr ( maxadd , 0 ) ;
}

void addedge ( int R , int L , int idx ) {
	edge -> L = L ;
	edge -> idx = idx ;
	edge -> next = H[R] ;
	H[R] = edge ++ ;
}

void pushup ( int o ) {
	old[o] = max ( old[ls] , old[rs] ) ;
	now[o] = max ( now[ls] , now[rs] ) ;
}

void pushdown ( int o ) {
	if ( add[o] || maxadd[o] ) {
		maxadd[ls] = max ( add[ls] + maxadd[o] , maxadd[ls] ) ;
		maxadd[rs] = max ( add[rs] + maxadd[o] , maxadd[rs] ) ;
		old[ls] = max ( old[ls] , now[ls] + maxadd[o] ) ;
		old[rs] = max ( old[rs] , now[rs] + maxadd[o] ) ;
		add[ls] += add[o] ;
		add[rs] += add[o] ;
		now[ls] += add[o] ;
		now[rs] += add[o] ;
		add[o] = maxadd[o] = 0 ;
	}
}

void update ( int L , int R , int v , int o , int l , int r ) {
	if ( L <= l && r <= R ) {
		now[o] += v ;
		add[o] += v ;
		maxadd[o] = max ( maxadd[o] , add[o] ) ;
		old[o] = max ( now[o] , old[o] ) ;
		return ;
	}
	int m = mid ;
	pushdown ( o ) ;
	if ( L <= m ) update ( L , R , v , lson ) ;
	if ( m <  R ) update ( L , R , v , rson ) ;
	pushup ( o ) ;
}

int query ( int L , int R , int o , int l , int r ) {
	if ( L <= l && r <= R ) return old[o] ;
	int m = mid , res = 0 ;
	pushdown ( o ) ;
	if ( R <= m ) return query ( L , R , lson ) ;
	if ( m <  L ) return query ( L , R , rson ) ;
	return max ( query ( L , R , lson ) , query ( L , R , rson ) ) ;
}

void solve () {
	int l , r ;
	clear () ;
	FOR ( i , 1 , n ) scanf ( "%d" , &num[i] ) ;
	scanf ( "%d" , &q ) ;
	rep ( i , 0 , q ) {
		scanf ( "%d%d" , &l , &r ) ;
		addedge ( r , l , i ) ;
	}
	FOR ( R , 1 , n ) {
		int x = num[R] + 100000 ;
		update ( pre[x] + 1 , R , num[R] , root ) ;
		pre[x] = R ;
		travel ( e , H , R ) ans[e -> idx] = query ( e -> L , R , root ) ;
	}
	rep ( i , 0 , q ) printf ( "%d\n" , ans[i] ) ;
}

int main () {
	while ( ~scanf ( "%d" , &n ) ) solve () ;
	return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值