bzoj 1012 [JSOI2008]最大数maxnumber

bzoj 1012 [JSOI2008]最大数maxnumber


Description
现在请求你维护一个数列,要求提供以下两种操作:
1、 查询操作。语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。限制:L不超过当前数列的长度。
2、 插入操作。语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。限制:n是非负整数并且在长整范围内。
注意:初始时数列是空的,没有一个数。
Input
第一行两个整数,M和D,其中M表示操作的个数(M <= 200,000),D如上文中所

Output
对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。
Sample Input
5 100
A 96Q 1
A 97
Q 1
Q 2
Sample Output
96
93
96


题外话

每次打题面总是很痛苦……有没有哪位大犇有种既快又美观的输入题面的方法,蒟蒻在线求教……


题解

这题我知道很多大犇肯定是用许多高大上的算法做的,想法很神奇!。但是呢,像我这种小蒟蒻,做完这道题有别的收获,因为我使用线段树做的。网上有很多线段树的代码,基本上都是略过,因为这种暴力的蒟蒻想法其它博主不屑去讲。但是我看到却如获至宝,因为这题的线段树想法就是完美(?)的线段树插入操作模版!那么下面我就详细讲讲这题的暴力思想,顺带也给自己复习一下线段树的插入操作(以这题为背景)。
线段树的插入操作其实并没有我们想的那么难,在本题中,一开始这颗线段树是空的,但是我们也可以认为是一颗所有节点权值为0的线段树,那么根据题意,我们处理Q这个操作时,无非就是线段是的询问操作,接下来的关键是A操作。首先我们由题目的值,每个测点中我们最多只需要建一颗节点个数为M的线段树即可,为什么呢,因为每个测点中只有M次操作,也就是说假设生成数据的人脑子出了问题偏爱插入操作,所有操作都是插入操作,那也只有M个节点被插入了这颗线段树中。其实线段树的插入操作我们可以不妨换种思维,假设要将权值为v的节点插入到数列的末尾,无非就是相当于将线段树相应的节点的权值从0改成v,那么就好理解了,所以我们可以将线段树的插入操作看成是一种变形的修改操作,或者根本就是一种修改操作。


奉上代码

#include
#include 
#define qmid(L,R) L+((R-L)>>1)
#define LL long long
using namespace std;
const int INFINITE=-4e18;
int n,tot;
LL st[4*200005],D;
inline int read_number(){
    LL ret=0;char ch=getchar();
    while (ch<'0'||ch>'9')ch=getchar();
    while (ch>='0'&&ch<='9')ret=ret*10+ch-48,ch=getchar();
    return ret;
}
inline char read_char(){
    char ret=getchar();
    while (ret=='\n'||ret==' ')ret=getchar();
    return ret;
}
inline void insert(int root,int L,int R,int k,int v){ //插入操作
    if (L==R){st[root]=v;return;} //如果到了叶子节点,那么就修改
    int mid=qmid(L,R);
    if (k<=mid)insert(root*2+1,L,mid,k,v);else insert(root*2+2,mid+1,R,k,v); //如果要插入的节点的位置小于mid那么就插入左儿子否则插入右儿子
    st[root]=max(st[root*2+1],st[root*2+2]);  //递归修改父亲节点
}
inline LL query(int root,int L,int R,int qL,int qR){  //询问操作
    if (L>qR||Rreturn INFINITE;  //如果当前区间完全和要询问的区间搭不着边,那么就退无限小(也就是再怎样程序也不会取的的值)
    if (L>=qL&&R<=qR)return st[root];  //如果当前区间包含在询问区间内,那么就退这个区间的极值
    int mid=qmid(L,R);
    return max(query(root*2+1,L,mid,qL,qR),query(root*2+2,mid+1,R,qL,qR));  //分别询问左儿子右儿子
}
int main(){
    n=read_number(),D=read_number();
    LL lst=0;
    for (int i=1;i<=n;i++){
        char ch=read_char();int x=read_number(); 
//      putchar(ch),printf(" %d\n",x);
        if (ch=='A')insert(1,1,n,++tot,(x+lst)%D);
               else lst=query(1,1,n,tot-x+1,tot),printf("%lld\n",lst);
    }
    return 0;
}
/*
Sample Input
5 100
A 96
Q 1
A 97
Q 1
Q 2
*/

后记

这道题目我是将它当做线段树插入操作的经典题目来做的,当然网上还有另外的神奇算法,其中感觉比较简单的是单调栈,大家也可以去看看,毕竟题目不能AC了就了事了,还需要去看看他人的想法,这样才可以打通奇经八脉进一步提升自己。


你可能感兴趣的:(题解)