POJ 3580 Super Memo (Treap版)

学自牛人博客

题意: 对一个序列进行区间增减,区间翻转,区间移动,插入,删除,求区间最小值。

解法: 正常情况下这种折磨序列的题一向是splay的菜,而这次发现,Treap原来也有这种战斗力,而且在编程复杂度上,个人感觉比splay简单一点。

其实就是原来的Treap加上了砍树与合并两个操作,把每次的操作区间‘砍’出来,一通乱搞完接回去。而splay是把区间旋转出来操作。时间复杂度方面,依靠于Treap的随机性,也就是nlogn啦。

/* **********************************************
Author      : Nero
Created Time: 2013-9-3 13:08:39
Problem id  : POJ 3580
Problem Name: SuperMemo
*********************************************** */

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define REP(i,a,b) for(int i=(a); i<(int)(b); i++)
#define clr(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f;

// w的大根堆
// Be careful for NULL point
// Treap
struct Node {
    Node *l,*r;
    int v,delta,rev,size,minx,w;
    void up() {
        minx = v;
        size = 1;
        if(l) size += l->size, minx = min(minx, l->minx);
        if(r) size += r->size, minx = min(minx, r->minx);
    }
    void down() {
        if(delta) {
            if(l) l->delta += delta, l->v += delta, l->minx += delta;
            if(r) r->delta += delta, r->v += delta, r->minx += delta;
            delta = 0;
        }
        if(rev) {
            swap(l,r);
            if(l) l->rev ^= 1;
            if(r) r->rev ^= 1;
            rev = 0;
        }
    }
}*root = NULL, *list = NULL;
inline int sz(Node *o) { return o ? o->size : 0; }
int ran() {
    static int ranx = 123456789;
    ranx += (ranx<<2) + 1;
    return ranx;
}

void New_node(Node *&o, int val) {
    if(list == NULL) {
        Node *tt = new Node[100];
        for(int i = 0; i < 100; i ++) {
            tt[i].w = ran();
            tt[i].r = list;
            list = tt + i;
        }
    }
    o = list;
    list = o->r;
    o->l = o->r = NULL;
    o->v = o->minx = val;
    o->size = 1;
    o->delta = o->rev = 0;
}
void Reuse(Node *o) { if(o) { o->r = list; list = o; } }

// 切树,把前num个节点划分到p,其他划分到q。
void cut(Node *o, Node *&p, Node *&q, int num) {
    if(num == 0) {
        p = NULL; q = o;
    } else if(num == sz(o)) {
        p = o; q = NULL;
    } else {
        o->down();
        if(num <= sz(o->l)) {
            q = o;
            cut(o->l,p,q->l,num);
            q->up();
        } else {
            p = o;
            cut(o->r,p->r,q,num-sz(o->l)-1);
            p->up();
        }
    }
}

void merge(Node *&o, Node *p, Node *q) {
    if(!p || !q) {
        o = p ? p : q;
    } else {
        if(p->w > q->w) {
            p->down();
            o = p;
            merge(o->r,p->r,q);
        } else {
            q->down();
            o = q;
            merge(o->l,p,q->l);
        }
        o->up();
    }
}

void insert(Node *&o, int pos, int val) {
    if(o == NULL) {
        New_node(o,val);
    } else {
        o->down();
        if(sz(o->l) >= pos) {
            insert(o->l,pos,val);
            if(o->l->w > o->w) {
                Node* temp = o;
                o = o->l;
                o->down();
                temp->l = o->r;
                o->r = temp;
                o->r->up();
            }
        } else {
            insert(o->r,pos-sz(o->l)-1,val);
            if(o->r->w > o->w) {
                Node* temp = o;
                o = o->r;
                o->down();
                temp->r = o->l;
                o->l = temp;
                o->l->up();
            }
        }
        o->up();
    }
}

void add(int l, int r, int val) {
    Node *a, *b, *c;
    cut(root,a,b,l-1);
    cut(b,b,c,r-l+1);
    b->v += val;
    b->minx += val;
    b->delta += val;
    merge(a,a,b);
    merge(root,a,c);
}

void remove(int pos) {
    Node *a, *b, *c;
    cut(root,a,b,pos-1);
    cut(b,b,c,1);
    merge(root,a,c);
    Reuse(b);
}

int query(int l, int r) {
    Node *a, *b, *c;
    cut(root,a,b,l-1);
    cut(b,b,c,r-l+1);
    int ret = b->minx;
    merge(a,a,b);
    merge(root,a,c);
    return ret;
}

void reverse(int l, int r) {
    Node *a, *b, *c;
    cut(root,a,b,l-1);
    cut(b,b,c,r-l+1);
    b->rev ^= 1;
    merge(a,a,b);
    merge(root,a,c);
}

void revolve(int l, int m, int r) {
    Node *a, *b, *c, *d;
    cut(root,a,b,l-1);
    cut(b,b,c,m-l+1);
    cut(c,c,d,r-m);
    merge(a,a,c);
    merge(a,a,b);
    merge(root,a,d);
}

int main() {
    int n,d,nq,a,b;
    char s[10];
    scanf("%d", &n);
    for(int i = 0; i < n; i ++) {
        scanf("%d", &d);
        insert(root,i,d);
    }
    scanf("%d", &nq);
    while(nq--) {
        scanf("%s", s);
        if(s[0] == 'A') {
            scanf("%d%d%d", &a, &b, &d);
            add(a,b,d);
        } else if(s[0] == 'I') {
            scanf("%d%d", &a, &d);
            insert(root,a,d);
        } else if(s[0] == 'D') {
            scanf("%d", &a);
            remove(a);
        } else if(s[0] == 'M') {
            scanf("%d%d", &a, &b);
            printf("%d\n", query(a,b));
        } else if(s[3] == 'E') {
            scanf("%d%d", &a, &b);
            reverse(a,b);
        } else {
            scanf("%d%d%d", &a, &b, &d);
            d = (d % (b-a+1) + (b-a+1)) % (b-a+1);
            if(d) revolve(a,b-d,b);
        }
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值