POJ 2452 Sticks Problem(二分+RMQ)

本文介绍了如何使用二分法和RMQ(线段树)解决POJ2452问题,重点在于通过扫描数组并找到特定条件下的最长区间。

POJ 2452 Sticks Problem(二分+RMQ)

http://poj.org/problem?id=2452

题意:

        Xuanxuan has n sticks of different length. One day, she puts all her sticks in a line, represented by S1, S2, S3, ...Sn. After measuring the length of each stick Sk (1 <= k <= n), she finds that for some sticks Si and Sj (1<= i < j <= n), each stick placed between Si and Sj is longer than Si but shorter than Sj. 
        Now given the length of S1, S2, S3, …Sn, you are required to find the maximum value j - i.

分析:

        我们从左到右依次扫描a[i],假设当前L为k,那么找到使得getMin(L,r)==L的最大r,那么区间[L,r]中的最大值位置即R=getMax(L,r)就是当L固定时的终点.所以当L固定时,最长区间为R-L+1.

找到使得getMin(L,r)==L的最大r,这个怎么求呢?这个的求法可以用二分法来求,因为如果存在r=100,使得上式成立,那么L<=r<100肯定都使上式成立.

        注意本题输出的是R-L,而不是R-L+1.

AC代码:1766ms

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 50000+100;
int a[MAXN];
int dmax[MAXN][20];//保存的是最大值的下标,且如果两个值相同,那么下标小的那个最大
int dmin[MAXN][20];//保存的是最小值的下标,且如果两个值相同,那么下标大的那个最小
int n;
int maxv(int i,int j)
{
    if(a[i]==a[j])
        return i<j?i:j;
    return a[i]<a[j]?j:i;
}
int minv(int i,int j)
{
    if(a[i]==a[j])
        return i<j?j:i;
    return a[i]<a[j]?i:j;
}
void initMax(int n,int d[])
{
    for(int i=1;i<=n;i++)dmax[i][0]=i;//dmax返回下标时,注意这里初始化的是i
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            dmax[i][j]=maxv(dmax[i][j-1] , dmax[i+(1<<(j-1))][j-1]);
}
int getMax(int L,int R)
{
    int k=0;
    while((1<<(k+1))<=R-L+1)k++;
    return maxv(dmax[L][k] , dmax[R-(1<<k)+1][k]);
}
void initMin(int n,int d[])
{
    for(int i=1;i<=n;i++)dmin[i][0]=i;
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            dmin[i][j]=minv(dmin[i][j-1] , dmin[i+(1<<(j-1))][j-1]);
}
int getMin(int L,int R)
{
    int k=0;
    while((1<<(k+1))<=R-L+1)k++;
    return minv(dmin[L][k] , dmin[R-(1<<k)+1][k]);
}
int get_r(int i)
{
    int l=i,r=n;
    while(r>l)
    {
        int mid=l+(r-l+1)/2;
        if(getMin(i,mid)==i)
            l=mid;
        else
            r=mid-1;
    }
    return l;
}
int main()
{

    while(scanf("%d",&n)==1)
    {
        if(n==0)
        {
            printf("-1\n");
            continue;
        }
        int ans=0;
         scanf("%d",&a[1]);
        for(int i=2;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]>a[i-1])
                ans=1;
        }
        if(ans==0)//非严格递减数列,不存在要求序列
        {
            printf("-1\n");
            continue;
        }
        initMax(n,a);
        initMin(n,a);
        int L,R,r;
        for(int i=1;i<=n;i++)
        {
            L=i;
            r=get_r(L);//找到使得getMin(L,r)==L的最大r
            R=getMax(L,r);
            ans=max(ans,R-L);
        }
        printf("%d\n",ans);
    }
    return 0;
}
</span>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值