ZSTU4239-巴比伦花园

巴比伦花园

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 335  Solved: 39

Description

巴比伦花园在前6世纪由新巴比伦王国的尼布甲尼撒二世(Nebuchadnezzar)在巴比伦城为其患思乡病的王妃安美依迪丝(Amyitis)修建的。园中种植各种花草树木,远看犹如花园悬在半空中。当然这个伟大的奇迹现在已经不存在了,但幸运的是我们仍能从流传于后世的故事中窥看到它的蛛丝马迹。

据说花园被建造的时候,花园的座落处有一块巨大的石头,聪明的工匠决定把它打造成一个天然的阶梯,因为这块石头原本就凹凸不平的,形如:h1 , h2, h3 ……, hn。但是局限于当时的技术条件,工匠们只能把一段连续的区间:hi, hi+1, hi+2, ……, hj 加工成:

fi, fi+1, fi+2, ……, fj ,并且要满足三个条件:

1. fi+1 = fi + k, 

2. fi>= 1,

3.fi <= hi.

Input

第一行有个整数T, 表示测试组数。T≦10。

接下来每个测试组,第一行给出两个数n,k, Q,表示区间的个数和相邻的两个阶梯的高度差和询问次数。

接下来一行有n个数,第i个数表示改区间的高度hi。

接下来Q行,每一行有两个数l r : 表示求[l , r]这个区间所能得到的最长合法阶梯的长度。

    1 <= l <= r <= n, 1 <= n <= 100000, 1 <= k <= 1e7,  1 <= hi <= 1e15

Output

请对每个询问都输出一行。

Sample Input

15 1 31 3 2 4 31 32 45 5

Sample Output

231

HINT

Source

解题思路:dp(i):表示从以h(i)为阶梯的起点,所能加工出的最长阶梯的最右端的位置,用线段树维护两个值(max{dp[i]-i+1}, max{dp[i]}),对于询问的区间pl, pr, 先在线段树上二分找到一个最左的dp[i]>=pr的位置l,然后在[pl, l-1]上询问一个最大的dp[i]-i+1


#include <stdio.h>   
#include <string.h>   
#include <stack>   
#include <algorithm>   
#include <iostream>   
#include <set>   
#include <map>   
#include <math.h>   
#include <queue>   
    
using namespace std;   
    
#define LL long long   
const int M = 100000+10;   
LL h[M];   
int R[M],n,k,m;   
    
struct node   
{   
    int mari,mar;   
} a[M<<2];   
    
void build (int k,int l,int r)   
{   
    if(l==r)   
    {   
        a[k].mari=R[l]-l+1;   
        a[k].mar=R[l];   
        return;   
    }   
    int mid=(l+r)>>1;   
    build(k<<1,l,mid);   
    build(k<<1|1,mid+1,r);   
    a[k].mari=max(a[k<<1].mari,a[k<<1|1].mari);   
    a[k].mar=max(a[k<<1].mar,a[k<<1|1].mar);   
}   
    
int f(int k,int l,int r,int ll,int rr)   
{   
    if(a[k].mar<rr) return 0;    
    if(l==r) return l;   
    int mid=l+r>>1,res;   
    if(ll<=mid&&(res=f(k<<1,l,mid,ll,rr))) return res;   
    if(rr>mid&&(res=f(k<<1|1,mid+1,r,ll,rr))) return res;   
}   
    
int get(int k, int l, int r, int ll, int rr)   
{   
    if (ll<=l&&r<=rr) return a[k].mari;   
    int mid=l+r>>1,res=0;   
    if (ll<=mid) res=max(res,get(k<<1,l,mid,ll,rr));   
    if (rr>mid) res=max(res,get(k<<1|1,mid+1,r,ll,rr));   
    return res;   
}   
    
int main ()   
{   
    int t;   
    scanf("%d",&t);   
    while(t--)   
    {   
        scanf("%d %d %d", &n,&k,&m);   
        for (int i=1; i<=n; i++)   
            scanf("%lld",&h[i]);   
        LL hh=1;   
        int x=1;   
        for (int i=1; i<=n; i++,hh+=k)   
        {   
            while(h[i]<hh)   
            {   
                R[x++]=i-1;   
                hh-=k;   
            }   
        }   
        for(int i=x; i<=n; i++) R[i]=n;   
        build(1,1,n);   
        int l,r;   
        while(m--)   
        {   
            scanf("%d %d",&l,&r);   
            int x=f(1,1,n,l,r);   
            printf("%d\n",max(get(1,1,n,l,x-1),r-x+1));   
        }   
    }   
    return 0;   
}   

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值