题目:luogu1198.
题目的意思就是维护两个操作:
1.格式Q L,表示查询末尾L个元素里的最大值.
2.格式A n,表示给末尾插入一个数,值为(n+t) mod D,其中D是给定常数,t是上一次查询的答案.
操作总次数M<=2*10^5.
其实这道题我很想知道为什么大佬们都不写线段树而写单调栈(就因为单调栈短?)或者splay(因为splay支持插入操作?).
用线段树的话我们发现每一次往后插入的数只有一个,且插入操作不多于M,说明最多线段树只需要开M*4个节点.
而题目让我们维护的是最大值,那我们给未插入的节点提前留下空位,未插入时值就设为-INF.
之后维护一下插入了i个数,每一次插入一个节点就直接给第i+1个节点加上(n+t) mod D就可以了.
至于末尾l个元素,直接查询区间[i-L+1,i]的元素最大值就好了.
那么这道题就很简单了,变成了线段树维护单点修改,区间维护最值的问题.
那么代码如下:
#include<bits/stdc++.h> using namespace std; const int INF=2147483647; const int N=300000; int m,D; struct tree{ int l,r,max; }tr[5*N]; void build(int L,int R,int k=1){ tr[k].l=L;tr[k].r=R; tr[k].max=-INF; if (L==R) return; int mid=L+R>>1; build(L,mid,k<<1); build(mid+1,R,k<<1|1); } void change(int x,int num,int k=1){ if (tr[k].l==x&&tr[k].r==x){ tr[k].max=num; return; } int mid=tr[k].l+tr[k].r>>1; if (x<=mid) change(x,num,k<<1); else change(x,num,k<<1|1); tr[k].max=max(tr[k<<1].max,tr[k<<1|1].max); } int query(int L,int R,int k=1){ if (tr[k].r<L||tr[k].l>R) return -INF; if (tr[k].r<=R&&tr[k].l>=L) return tr[k].max; return max(query(L,R,k<<1),query(L,R,k<<1|1)); } inline void into(){ scanf("%d%d",&m,&D); } inline void work(){ } inline void outo(){ build(1,N); char opt; int L,now=0,n,last=0; for (int i=1;i<=m;i++){ cin>>opt; if (opt=='Q') { scanf("%d",&L); printf("%d\n",last=max(query(now-L+1,now),0)); //不跟0去比较会错,估计luogu的数据有问题 }else{ scanf("%d",&n); ++now; change(now,(n+last)%D); } } } int main(){ into(); work(); outo(); return 0; }
blog一百篇纪念.