DRUIDEOI

题目链接:http://www.spoj.com/problems/DRUIDEOI/

题意大概是给定n个数字,找到每一个数字左边第一个比他大的数字,再找到右边第一个比他大的数字。输出他们的位置,如果不存在就输出-1

而且构成一个环,也就是说比如1 3 3 3,那么第一个数字的答案则是,4,2。    n<=1e5


这个n有点大,暴力显然是不太行的,肯定时间上是不接受的。之前学过单调栈这个东西,他就是可以用来求最左边和最右边第一个大于(小于)它的数字的。

但是这道题是环形的,我们就可以用数组倍增来模拟一下,单调栈的具体原理之前也学习过了。

倍增的具体过程这样:比如 2 3 3 1 ,倍增后变成2 3 3 1 2 3 3 1 ,那么在第一个数组中的2肯定能找到他右边第一个比他大的数字,但是它却不一定能找到左边第一个比它大的数字,而第二个数组中的2就一定可以,所以我们可以让第二个数组中的2左边第一个比他大的数的位置(%n)作为答案,同理第一个数组中的数字都能找到其右边界(如果 存在的话),第二个数组中的数字都能找到其左边界(如果存在的话)。这样我们就解决了环形的问题;

还有一个问题就是比如,1 3 3 5,在朴素的单调栈中第二个3被弹出来之后就确定了其右边界也就是右边第一个≥它的数字的位置 3 。但我们要找的是>它的, 这里右边界显然是找不对的。

具体操作中可以加一个判断 , 如果被弹出的栈顶元素与将入栈的元素是相等的话,给被弹出的元素一个标记,(具体我把他的右边界值记为-10) ,然后在做完单调栈之后,我们 再从右往左遍历一下这个数组, 如果当前遍历到的元素的值等于其右边元素的值且被标记过了,那么这个元素的右边界等于其右边元素的右边界。这样就解决了右边界不正确的问题。

贴一下代码,特别混乱。。。。

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const int MOD=1e9+7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
struct Stack
{
    int l , r, val ,p;
} stac[300300];
Stack h[300300];
struct Ans
{
    int l ,r;
}ans[300300];
int p1 , p2;
void PUSH(Stack  num  )
{
    while( p2 >= p1 && stac[p2].val <= num.val)
    {
        int p = stac[p2].p ;
        if(stac[p2].val != num.val)
        ans[p].r = num.p ;
        else ans[p].r = -10;

        num.l = stac[p2].l;
        p2 -- ;
    }
    num.l = -1;
    if(p1 <= p2)
        num.l = stac[p2].p ;

    ans[num.p].l = num.l ;
   // if(num.p == 2)cout<<num.l<<endl;
    stac[++ p2] = num ;
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        p1 = 1 , p2 = 0;
        clrI(ans);
        clrI(stac);
        int n ;
        scanf("%d",&n);

        for(int i = 1; i<=n ;++ i)
        {
            scanf("%d",&h[i].val);
            h[i].l = i;
            h[i].r = i;
            h[i].p = i;
            h[i + n].val = h[i].val;
            h[i+ n ].l = h[i].l +n;
            h[i + n ].r = h[i].r + n;
            h[i + n ].p = h[i].p + n;
        }
        for(int i = 1; i <=2 * n ;++i)
            PUSH(h[i]);
        h[ 2*n + 1].val = INF ;
        h[2 * n + 1].r = -1;
        h[2*n+1].r = -1;
        h[2*n+1].p  = -1 ;
        PUSH(h[2*n+1]);
   // for(int i =1; i<= 2 * n ;++ i)printf("%d %d\n",ans[i].l,ans[i].r);
    for(int i = 2 * n ; i >=1 ; --i)if(ans[i].r == -10 && h[i].val == h[i+1].val) ans[i].r = ans[i+1].r ;
    //for(int i = 1; i<= 2*n;++i)if(ans[i].l == -10) ans[i].l = ans[i-1].l;
        for(int i = n + 1 ; i<= 2*n ;++i)
        {
            if(ans[i].r == -10)ans[i].r = -1;
            if(ans[i].l == -10) ans[i].l = -1;
            if(ans[i - n].r == -10)ans[i - n].r = -1;
            if(ans[i - n].l == -10) ans[i-n].l = -1;
            if(ans[i].l != -1)
            ans[i-n].l = (ans[i].l %n)==0?n:(ans[i].l % n );
            if ( ans[i].r != -1)
            ans[i-n].r = (ans[i].r %n)==0?n:(ans[i].r % n );
            ans[i-n].l = (ans[i- n ].l %n)==0?n:(ans[i- n].l % n );
            ans[i-n].r = (ans[i-n].r %n)==0?n:(ans[i- n ].r % n );
            if(ans[i-n].r == i-n) ans[i-n].r = -1;
            if(ans[ i - n].l == i- n) ans[ i - n].l = -1;
        }
        for(int i = 1 ; i<= n ;++i)printf("%d %d\n",ans[i].l,ans[i].r);
    }
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值