bzoj 1012

典型数据结构题,可用线段树、ST表、栈、树状数组。

线段树就不说了,裸的一比,也不难打,就是跑得有点慢。

#include<cstdio>
#include<cctype>
#include<algorithm>
#define mid (l+r>>1)
#define lc (o<<1)
#define rc (o<<1)+1
using namespace std;
int read(){
    char c; while(!isdigit(c=getchar())); int x=c-'0';
    while(isdigit(c=getchar())) x=x*10+c-'0'; return x;
}
int cnt,t,maxv[1<<19];
void change(int o,int l,int r,int w,int v){
    if(l>w || r<w) return;
    if(l==r){maxv[o]=v; return;}
    change(lc,l,mid,w,v);
    change(rc,mid+1,r,w,v);
    maxv[o]=max(maxv[lc],maxv[rc]);
}
int search(int o,int l,int r,int L,int R){
    if(l>R || r<L) return 0;
    if(l>=L && r<=R) return maxv[o];
    return max(search(lc,l,mid,L,R),search(rc,mid+1,r,L,R));
}
int main(){
    int m=read(),d=read();
    for(int i=1;i<=m;i+=1){
        char c; while(c=getchar(),c!='A' && c!='Q');
        if(c=='A') change(1,1,m,++cnt,((long long)read()+t)%d);
        else printf("%d\n",t=search(1,1,m,cnt-read()+1,cnt));
    }
    return 0;
}

ST表在网上貌似不多,这个数据结构是天生支持从末尾插入的。

代码很短,就是内存需要得比较大,跑得还是挺快的。

#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
using namespace std;
int read(){
    char c; while(!isdigit(c=getchar())); int x=c-'0';
    while(isdigit(c=getchar())) x=x*10+c-'0'; return x;
}
int f[200001][19];
int main(){
    int n=0,m=read(),d=read(),t=0;
    while(m--){
        char opt; while(opt=getchar(),opt!='A' && opt!='Q');
        if(opt=='A'){
            f[++n][0]=((long long)read()+t)%d;
            for(int i=1;(1<<i)<=n;i+=1)
                f[n][i]=max(f[n-(1<<i-1)][i-1],f[n][i-1]);
        }else{
            int l=read(),x=log2(l);
            t=max(f[n-l+(1<<x)][x],f[n][x]);
            printf("%d\n",t);
        }
    }
    return 0;
}

由于询问一定是询问的最后几个中的最大值,所以我们可以维护单调性,保证栈顶到栈底单调递增。

输出时二分一下可以取的在栈中位置最底下的那个元素。

这是我写的这四个方法中跑得最快的一个,代码也很短。

#include<cstdio>
#include<cctype>
int read(){
    char c; while(!isdigit(c=getchar())); int x=c-'0';
    while(isdigit(c=getchar())) x=x*10+c-'0'; return x;
}
int cnt,top,t,s[200001][2];
int main(){
    int m=read(),d=read();
    while(m--){
        char c; while(c=getchar(),c!='A' && c!='Q');
        if(c=='A'){
            int x=((long long)read()+t)%d;
            while(top && s[top][0]<=x) top--;
            s[++top][0]=x; s[top][1]=++cnt;
        }else{
            int l=1,r=top,v=read();
            while(l<=r){
                int mid=l+r>>1;
                if(s[mid][1]>cnt-v) t=s[mid][0],r=mid-1; else l=mid+1;
            }
            printf("%d\n",t);
        }
    }
    return 0;
}

树状数组的做法貌似也不多。

先统计有多少次增加元素。

然后编号倒一下,第一个变成最后一个,以此类推。

然后直接树状数组维护即可。

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
int read(){
    char c; while(!isdigit(c=getchar())); int x=c-'0';
    while(isdigit(c=getchar())) x=x*10+c-'0'; return x;
}
int tot,t,opt[200001],v[200001],tree[200001];
int main(){
    int m=read(),d=read();
    for(int i=1;i<=m;i+=1){
        char c; while(c=getchar(),c!='A' && c!='Q');
        if(c=='A') opt[i]=1,tot++; else opt[i]=2;
        v[i]=read();
    }
    int now=tot+1;
    for(int i=1;i<=m;i+=1)
        if(opt[i]==1){
            int add=((long long)v[i]+t)%d;
            for(int j=--now;j<=tot;j+=j&-j) tree[j]=max(tree[j],add);
        }else{
            t=0;
            for(int j=now+v[i]-1;j>=now;j-=j&-j) t=max(tree[j],t);
            printf("%d\n",t);
        }
    return 0;
}
原文地址:https://www.cnblogs.com/AmnesiacVisitor/p/7591583.html