【JSOI2008】最大数的线段树解题

题目: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一百篇纪念.

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/80535050