洛谷 P1198 [JSOI2008]最大数

啊……也不知道到底能不能拿省一
机房都开始学Treap和Splay了

emm……然后我在洛谷找到一道之前A了的提高省选题做(可能是水题……)
号称是“树状数组和线段树的基础”(我感觉比线段树还难)

不过如果这道题我真的是用线段树写的话,我肯定就不会来写博客了2333
所以我们来看看这道有趣的题2333

[JSOI2008]最大数
题目描述

现在请求你维护一个数列,要求提供以下两种操作:

1、 查询操作。

语法:Q L

功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。

限制:L不超过当前数列的长度。

2、 插入操作。

语法:A n

功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。

限制:n是整数(可能为负数)并且在长整范围内。

注意:初始时数列是空的,没有一个数。

输入输出格式

输入格式:
第一行两个整数,M和D,其中M表示操作的个数(M <= 200,000),D如上文中所述。

接下来的M行,每行一个字符串,描述一个具体的操作。语法如上文所述。

输出格式:
对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。

输入输出样例

输入样例#1:
5 100
A 96
Q 1
A 97
Q 1
Q 2
输出样例#1:
96
93
96

不同寻常的,我现在要直接说出神奇做法了
有个写题解的人号称这个做法叫“单调栈”…….?
反正第一次抄的时候是因为它短………emm…….
现在写博客是因为发现用这个方法才应该是我们应该学的。
而我想把这个做法应该是…….用单调栈把问题转化成一个二分答案题╮(╯▽╰)╭

简解:【其实自己想或者直接看代码可能更有助于思维emmm】
先开两个栈。第一个栈存储在队列里的位置,第二个栈存储这个数字的大小

每当有新元素要入队的时候,我们可以把排在这个元素前的比它小的数直接忽略了(因为查询的区间永远都是后L个数,所以…….)然后更新一下两个栈。

接下来我们就可以二分栈的下标,直到二分到一个下标i,使stack[2][i]表示的队列位置在所求的区间里,那么答案就有了。

代码:

#include
#include
#include
using namespace std;
#if 0
Writers: Goes && G.S.M.
Have falled in love
#endif
#define ll long long 
inline ll read(){
    char ch=getchar();ll sum=0,f=1;
    while(ch<'0'||ch>'9'){
        ch=getchar();
        if(ch=='-') f=-1;}
    while(ch>='0'&&ch<='9')
        sum*=10,sum+=ch-'0',ch=getchar();
    return sum*f;
}
const int N=200005;
ll m,MO,t;
ll stack[2][N],top,cnt;
void add(int k){//添加元素
    cnt++;
    while(k>stack[1][top]&&top>0) top--;
    stack[0][++top]=cnt;
    stack[1][top]=k;
}
void query(int emm,int l,int r){//查询
    while(lint mid=(l+r)/2;
        if(stack[0][mid]1;
        else r=mid;
    }t=stack[1][r];
    printf("%lld\n",t);
}
int main()
{
    m=read(),MO=read();
    while(m--){
        char ch=getchar();
        while(ch!='A'&&ch!='Q') ch=getchar();
        if(ch=='A') add((t+read())%MO);
        else query(cnt-read()+1,1,top);
    }
}

你可能感兴趣的:(nlogn算法)