题面

Description

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

Input

  第一行两个整数,M和D,其中M表示操作的个数(M <= 200,000),D如上文中所述,满足D在longint内。接下来
M行,查询操作或者插入操作。

Output

  对于每一个询问操作,输出一行。该行只有一个数,即序列中最后L个数的最大数。

Sample Input

5 100
A 96
Q 1
A 97
Q 1
Q 2

Sample Output

96
93
96
 

题解

这里提供两种做法,单调队列和线段树

先说一下线段树解法

一.线段树解法

首先看到这道题就能想到线段树吧

这道题的线段树代码也很容易打,毕竟都没有lazy标记,就是单纯的点修改

然后建树函数也可以不打的..这道题的插入其实就是一个建树的过程,如果不打建树函数的话代码前面memset一下-inf就好

下面是代码:

 

#include <cstdio>
#include <cstring>
#define ll long long
#define inf 0x7fffffff
#define il inline 
il ll max(ll x,ll y){return x>y?x:y;}
il ll min(ll x,ll y){return x<y?x:y;}
il ll abs(ll x){return x>0?x:-x;}
il void swap(ll &x,ll &y){ll t=x;x=y;y=t;}
il void read(ll &x){
    x=0;ll f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    x*=f;
}
using namespace std;
/*===================Header Template=====================*/
#define N 200010
ll t[N<<2],n,mod,len=0,T=0;
void update(ll L,ll c,ll l,ll r,ll rt){
    if(l==r){t[rt]=c;return;}
    ll mid=(l+r)>>1;
    if(L<=mid)update(L,c,l,mid,rt<<1);
    else update(L,c,mid+1,r,rt<<1|1);
    t[rt]=max(t[rt<<1],t[rt<<1|1]);
}
ll query(ll L,ll R,ll l,ll r,ll rt){
    if(L<=l&&R>=r)return t[rt];
    ll mid=(l+r)>>1,ans=-inf;
    if(L<=mid)ans=max(ans,query(L,R,l,mid,rt<<1));
    if(R>mid)ans=max(ans,query(L,R,mid+1,r,rt<<1|1));
    return ans;
}
int main(){
    memset(t,128,sizeof(t));
    read(n);read(mod);
    for(ll i=1;i<=n;i++){
        char c=getchar();
        ll num;read(num);
        if(c=='A'){
            len++;
            update(len,(num+T)%mod,1,n,1);
        }else {
            if(num){
                T=query(len-num+1,len,1,n,1)%mod;
                printf("%lld\n",T);
            }else {
                printf("0\n");
                T=0;
            }
        }
    }
    return 0;
}

 

 

 

 

  

 

然而这道题是很玄学的,你把这个代码扔到洛谷上面会发现全部点MLE,然后在bzoj是能ac的...

如果要在洛谷提交就看一下我的代码理解一下思路自己试着打一个,或者说看看下面的单调队列写法

二.单调队列解法

 单调队列的写法就很简单了,但是很难想到这个做法,毕竟如果学过线段树第一时间想到的也会是线段树吧

这里维护一下最大值就好

 

#include <cstdio>
#include <cstring>
#define ll int
#define inf 1<<30
#define il inline 
il ll max(ll x,ll y){return x>y?x:y;}
il ll min(ll x,ll y){return x<y?x:y;}
il ll abs(ll x){return x>0?x:-x;}
il void swap(ll &x,ll &y){ll t=x;x=y;y=t;}
il void read(ll &x){
    x=0;ll f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    x*=f;
}
using namespace std;
/*===================Header Template=====================*/
#define N 200010
ll mx[N],a[N],p,m,mod,t,l=0;
char c[1];
int main(){
    read(m);read(mod);
    while(m--){
        scanf("%s%d",c,&p);
        if(c[0]=='A'){
            a[++t]=(l+p)%mod;
            for(ll i=t;i;i--){
                if(mx[i]<a[t])mx[i]=a[t];
                else break;
            }
        }else printf("%d\n",l=mx[t-p+1]);
    }
    return 0;
}

 


转载请注明出处:https://www.cnblogs.com/henry-1202/p/9090699.html