POJ - 3264(一个倔强的线段树男人)

虽然听说这是一道RMQ裸题,但是作为一个男人就要接受挑战。(其实是想记住这一种预处理的方式,否则也不会记录这么个RMQ裸题)

证明线段树确实能搞定它

本题就是求L,R内 最长 连续 相同数字 的长度

因为线段树能做区间max,min,和sum

进行O(3 * N)的预处理就可以直接O(lgN)查询了

第一步

把每段连续区间的最后一位(我们命名为计数点)记录这段连续区间长度

第二步

记录连续区间的点(非计数点),在这段区间内的位置 以负数形式记录下来

第三步

记录 每个非计数点 到这个区间的 计数点 需要的距离

预处理过程已经结束

然后直接线段树查 [ L , R ]区间内的最值Max,并且注意   左右两端的  不完整区间   是不是比  [L,R]内的完整区间长(L,R内都是相同数字的情况 要和你查询的 Max 取个最大值)

(橙色区域就是不完整区间)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <cmath>
#include <map>
#include <queue>
#include <algorithm>
#include <set>
#include <vector>
#include <stack>
#define Clear( x , y ) memset( x , y , sizeof(x) );
#define Qcin() std::ios::sync_with_stdio(false);
using namespace std;
typedef long long LL;
const int Maxn = 1e5 + 7;
const int Inf = 1e9 + 7;
LL A[Maxn];
LL C[Maxn] , To[Maxn];
LL N , Q;
struct edge{
	LL l , r;
	LL max , lazy;
}tree[Maxn * 5];

void PushUp( LL x ){
	tree[x].max = max( tree[x*2].max , tree[x*2+1].max);
}

void Build( LL l , LL r , LL x ){
	tree[x].l = l , tree[x].r = r;
	if( l == r ){
		tree[x].max = A[l];
		return;
	}
	LL mid = ( l + r ) / 2;
	Build( l , mid , x * 2 );
	Build( mid + 1 , r , x * 2 + 1 );
	PushUp( x );
}

LL Query( LL L , LL R , LL x ){
	if( L <= tree[x].l && tree[x].r <= R ){
		return tree[x].max;
	}
	LL mid = ( tree[x].l + tree[x].r ) / 2;
	LL res = 0LL;
	if( L <= mid )	res = max( res , Query( L , R , x * 2 ) );
	if( R > mid )	res = max( res , Query( L , R , x * 2 + 1 ) );
	return res;
}

void OPT(){
	for(int i = 1 ; i <= N ; i++){
		printf("%lld ", A[i]);
	}
	printf("\n");
	for(int i = 1 ; i <= N ; i++){
		printf("%lld ", To[i]);
	}
	printf("\n");
}

int main()
{
	while(~scanf(" %lld",&N) && N){
		scanf(" %lld",&Q);
		Clear(A , 0);	Clear(To , 0);
		for(LL i = 1 ; i <= N ; i++){
			scanf(" %d",&C[i]);
		}
		if(N == 1)	A[1] = 1;
		C[N+1] = 1000007;
		LL cnt = 1LL;
		for(LL i = 2 ; i <= N+1 ; i++){
			if(C[i] == C[i-1]){
				cnt++;
			} else{
				A[i-1] = cnt;
				cnt = 1LL;
			}
		}
		cnt = -1;
		for(LL i = 1LL ; i <= N ; i++){
			if(A[i]){
				cnt = -1;
			} else{
				A[i] = cnt;
				cnt--;
			}
		}
		for(LL i = N ; i >= 1 ; i--){
			if(A[i] >= 0){
				cnt = A[i];
			} else{
				To[i] = cnt + A[i];
			}
		}
		Build(1LL , N , 1LL);
		LL L , R , dL , ans;
		while(Q--){
			scanf(" %lld %lld",&L , &R);
			if(To[L-1] >= R - L + 1){
				printf("%lld\n",R - L + 1);
				continue;
			} else{
				dL = L-1 + To[L-1] + 1;
				ans = max( dL-L , Query(dL , R , 1LL) );
				if(A[R] < 0){
					if(-A[R] > R-L+1)	ans = max(ans , R-L+1);
					else	ans = max(ans , -A[R]);
				}
				printf("%lld\n",ans);
			}
		}
	}
}

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值