JSOI2008最大数maxnumber(栈&二分查找)

题目链接: 1012: [JSOI2008]最大数maxnumber

题意

中文题,点链接看吧,就不copy了。

思路

打眼一看立刻就想到线段树,但本题的区间最值查找每次都是在查后L位,感觉用线段树有些大材小用了。
再仔细想想,发现,如果倒数第i个比倒数第i+1个数小,那么第i个数是没有用的,任意查询的最值都不会是它,因为查的是后L个嘛。
所以呢,我们我以维护一个栈,每次添加新元素时,将其与栈顶元素比较,若栈顶元素小,即无用了,那就出栈,否则,就将新元素入栈。
在查询时直接对栈里元素进行二分查找就好了,假如我们要查询后L位的最值,那么区间第一个数是d[len-L]。
我们只需找出栈中大于等于len-L的最小值,这个值就是区间最值的下标。(栈里当然存下标,要不然何谈二分)

代码

#include <iostream>
#include <cstring>
#include <math.h>
#include <cstdio>
#include <stack>

using namespace std;

const int N = 99999;
int d[N];
int sk[N];

int main()
{
    char s[2];
    int x, n, mod, t, len=0, a=0;
    scanf("%d%d", &n, &mod);
    int st = 0;
    while(n--)
    {
        scanf("%s%d", s, &x);
        if(s[0] == 'A')
        {
            d[len] = (x+a)%mod;
            while(st > 0)
            {
                int temp = sk[st];
                if(d[temp] > d[len])
                    break;
                st--;
            }
            sk[++st] = len;
            len++;
        }
        else
        {
            int z = len-x;
            int l = 1;
            int r = st;
            while(l < r)
            {
                int mid = (l+r)>>1;
                if(sk[mid] < z)
                    l = mid+1;
                else
                    r = mid;
            }
            a = d[sk[r]];
            printf("%d\n", d[sk[r]]);
        }
    }
    return 0;
}