bzoj1251 序列终结者(Splay Tree+懒惰标记)

 

Description

网上有许多题,就是给定一个序列,要你支持几种操作:A、B、C、D。一看另一道题,又是一个序列 要支持几种操作:D、C、B、A。尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技术含量……这样 我也出一道题,我出这一道的目的是为了让大家以后做这种题目有一个“库”可以依靠,没有什么其他的意思。这道题目 就叫序列终结者吧。

【问题描述】 给定一个长度为N的序列,每个序列的元素是一个整数(废话)。要支持以下三种操作: 1. 将[L,R]这个区间内的所有数加上V。 2. 将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1。 3. 求[L,R]这个区间中的最大值。 最开始所有元素都是0。

Input

第一行两个整数N,M。M为操作个数。 以下M行,每行最多四个整数,依次为K,L,R,V。K表示是第几种操作,如果不是第1种操作则K后面只有两个数。

Output

对于每个第3种操作,给出正确的回答。

Sample Input

4 4
1 1 3 2
1 2 4 -1
2 1 3
3 2 4

Sample Output

2
【数据范围】
N<=50000,M<=100000。

 

【思路】

       Splay Tree+懒惰标记。

       用Splay Tree维护序列,添加flip,maxv,addv作为结点信息,其中flip与addv是懒惰标记,需要在Splay中调用pushdown下传标记。对于每次操作,将区间lr分裂出来后执行即可。

【代码】

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 using namespace std;
  5 
  6 const int maxn = 50000+10;
  7 const int INF = 1e9;
  8 inline int read() {
  9     char c=getchar();
 10     while(!isdigit(c)) c=getchar();
 11     int x=0;
 12     while(isdigit(c)) {
 13         x=x*10+c-'0';
 14         c=getchar();
 15     }
 16     return x;
 17 }
 18 
 19 struct Node {
 20     Node* ch[2];
 21     int v,s,flip,addv,maxv;
 22     int cmp(int k) const {
 23         int d=k - ch[0]->s;
 24         if(d==1) return -1;
 25         return d<=0? 0:1;
 26     }
 27     void pushdown() {
 28         if(addv) {
 29             v+=addv;
 30             ch[0]->addv+=addv,ch[1]->addv+=addv;
 31             addv=0;
 32         }
 33         if(flip) {
 34             flip=0;  swap(ch[0],ch[1]);  ch[0]->flip^=1,ch[1]->flip^=1;
 35         }
 36     }
 37     void maintain() {
 38         s=ch[0]->s+ch[1]->s+1;
 39         maxv=max(ch[0]->v,ch[1]->v),maxv=max(maxv,v);
 40     }
 41 };
 42 Node* null=new Node();
 43 void rotate(Node* &o,int d) {
 44     Node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d],k->ch[d]=o;
 45     o->maintain(),k->maintain(); o=k;
 46 }
 47 void splay(Node* &o,int k) {    //splay 的同时pushdown 下传标记
 48     o->pushdown();
 49     int d=o->cmp(k);
 50     if(d==1) k-=o->ch[0]->s+1;
 51     if(d!=-1) {
 52         Node* p=o->ch[d];
 53         p->pushdown();
 54         int d2=p->cmp(k);
 55         int k2=d2==1? k-p->ch[0]->s-1:k;
 56         if(d2!=-1) {
 57             splay(p->ch[d2],k2);
 58             if(d==d2) rotate(o,d^1);  else rotate(o->ch[d],d);
 59         }
 60         rotate(o,d^1);
 61     }
 62 }
 63 Node* merge(Node* left,Node* right) {
 64     splay(left,left->s);
 65     left->ch[1]=right,left->maintain();
 66     return left;
 67 }
 68 void split(Node* o,int k,Node* &left,Node* &right) {
 69     splay(o,k);
 70     left=o,right=left->ch[1],left->ch[1]=null,left->maintain();
 71 }
 72 struct SplaySeq {
 73     int n;
 74     Node *root,seq[maxn];
 75     Node* build(int sz) {
 76         if(!sz) return null;
 77         Node* l=build(sz/2);
 78         Node* o=&seq[++n];
 79         o->v=0,o->maxv=-INF; 
 80         o->ch[0]=l,o->ch[1]=build(sz-sz/2-1);
 81         o->flip=o->s=0;
 82         o->maintain();
 83         return o;
 84     }
 85     void init(int sz) {
 86         n=null->s=null->addv=0;
 87         null->v=null->maxv=-INF;
 88         root=build(sz);
 89     }
 90 }ss;
 91 
 92 int n,m;
 93 int main() {
 94     n=read(),m=read();
 95     ss.init(n+1);
 96     int k,l,r,v;
 97     Node *left,*right,*mid;
 98     for(int i=0;i<m;i++) {
 99         k=read(),l=read(),r=read();
100         split(ss.root,l,left,right),split(right,r-l+1,mid,right);
101         switch(k) {
102             case 1:
103                 v=read(); mid->addv+=v;
104                 break;
105             case 2:
106                 mid->flip^=1;
107                 break;
108             case 3:
109                 printf("%d\n",mid->maxv);
110                 break;
111         }
112         ss.root = merge(merge(left,mid),right);
113     }
114     return 0;
115 }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值