POJ3580 SuperMemo(Splay的区间操作)

传送门
第一次写Splay的区间操作,调了5+小时……
Splay区间操作的核心就是区间的提取
例如:要提取[5, 12] 就要把4 Splay到根,把13 Splay到4的右儿子,那么13的左儿子就是[5, 12]的所有信息。

代码:

#include<cstdio>
#define MAXN 200005
int tmp;
inline void Swap(int &a, int &b) { tmp = a; a = b; b = tmp; }
struct node {
    int sz, ch[2], f, v, mn, dv; bool rev; //mn:子树中的最小值,rev:是否经过翻转, dv:delta v,区间增减值的懒标记
    inline void mt() {Swap(ch[0], ch[1]);}
}t[MAXN];
int rt, sz, n, m;
inline void pushdown(int y) {
    if(t[y].rev) {t[y].mt(); if(t[y].ch[0]) t[t[y].ch[0]].rev ^= 1; if(t[y].ch[1]) t[t[y].ch[1]].rev ^= 1; t[y].rev = 0;}
    if(t[y].dv != 0) {t[y].v += t[y].dv; if(t[y].ch[0]) t[t[y].ch[0]].dv += t[y].dv; if(t[y].ch[1]) t[t[y].ch[1]].dv += t[y].dv; t[y].dv = 0;}
}
inline int Min(int a, int b) {return a < b ? a : b;}
inline void Upd(int r) {
    t[r].sz = t[t[r].ch[0]].sz + t[t[r].ch[1]].sz + 1;
    t[r].mn = Min(t[t[r].ch[0]].mn + t[t[r].ch[0]].dv, Min(t[t[r].ch[1]].mn + t[t[r].ch[1]].dv, t[r].v + t[r].dv));
}
inline void rot(int x)
{
    int y = t[x].f, z = t[y].f;
    bool f = (t[y].ch[1] == x);
    pushdown(y);pushdown(x);
    t[y].ch[f] = t[x].ch[f^1];
    if(t[y].ch[f]) t[t[y].ch[f]].f = y;
    t[x].ch[f^1] = y; t[y].f = x;
    t[x].f = z;
    if(z) t[z].ch[t[z].ch[1]==y] = x;
    Upd(y);
}
void Splay(int r, int tp, int &Root = rt) {
    for(int y, z; (y = t[r].f) != tp; rot(r)) {
        pushdown(r);
        z = t[y].f;
        if(z == tp) continue;
        if( (t[z].ch[0] == y) == (t[y].ch[0] == r) ) rot(y);
        else rot(r);
    }
    if(!tp) Root = r; Upd(r);
}
int Kth(int x, int &Root = rt)
{
    int y = Root, p;
    if(x>t[Root].sz)return 0;
    while(1)
    {
        pushdown(y); Upd(y);
        p=t[y].ch[0];
        if(t[p].sz+1<x) {
            x-=t[p].sz+1;
            y=t[y].ch[1];
        }
        else if(t[p].sz>=x) y=p;
        else break;
    }
    Splay(y, 0, Root);
    return y;
}
int a[MAXN];
int Build(int l, int r) {     //把初始区间建成二叉树
    if(l > r) return 0;
    int mid = (l + r) >> 1;
    t[mid].ch[0] = Build(l, mid-1);
    t[mid].ch[1] = Build(mid+1, r);
    t[mid].v = t[mid].mn = a[mid];
    Upd(mid); if(t[mid].ch[0]) t[t[mid].ch[0]].f = mid; if(t[mid].ch[1]) t[t[mid].ch[1]].f = mid;
    return mid;
}
inline void Rev(int l, int r) {  //区间翻转
    int b = Kth(r+2), a = Kth(l);
    Splay(b, a); int p = t[b].ch[0];
    t[p].rev ^= 1;
}
inline void Add(int l, int r, int w) {//区间增加
    int b = Kth(r+2), a = Kth(l);
    Splay(b, a); int p = t[b].ch[0];
    t[p].dv += w; Upd(b); Upd(a);
}
inline int QMIN(int l, int r) {//区间最小值
    int b = Kth(r+2), a = Kth(l);
    Splay(b, a); int p = t[b].ch[0]; Upd(b); Upd(a);
    return t[p].mn + t[p].dv;  //一定要加t[p].dv!!!
}
inline void Merge(int &left, int right) {//区间合并
    int p = rt;
    while(t[p].ch[1]) p = t[p].ch[1];
    Splay(p, 0, left);
    t[left].ch[1] = right; t[right].f = left;
    Upd(left);
}
inline void split(int &x, int k, int &left, int &right) {//区间分离
    Kth(k, x); left = x;
    right = t[x].ch[1]; t[right].f = 0;
    t[x].ch[1] = 0;
    Upd(left);
}
void Revolve(int l, int r, int T) //题目中定义的Revolve操作
{
    T = T%(r-l+1)+(r-l+1);        //discuss里面说T可能为负数……
    T %= (r-l+1);
    if (!T) return;
    Rev(l, r); Rev(l, l+T-1);
    Rev(l+T, r);
}
void Del(int k)
{
    int t1, t2, t3;
    split(rt, k, t1, t2);
    split(t2, 1, t2, t3);
    Merge(t1, t3);
    rt = t1;
}
char c, f;
inline void GET(int &n) {
    n = 0; f = 1;
    do {c = getchar(); if(c == '-') f = -1;} while(c > '9' || c < '0');
    while(c >= '0' && c <= '9') {n=n*10+c-'0';c=getchar();}
    n *= f;
}
void Ins(int k, int x)
{
    ++ sz;
    t[sz].v = t[sz].mn = x;
    t[sz].sz = 1; int t1, t2;
    split(rt, k+1, t1, t2);
    Merge(t1, sz);
    Merge(t1, t2);
    rt = t1;
}
int main() {
    int t1, t2, t3;
    GET(n); a[1] = a[n+2] = ~0U>>2;
    t[0].mn = ~0U>>2;
    for(int i = 1; i <= n; i ++) GET(a[i+1]);
    rt = Build(1, n+2); sz = n+4;
    GET(m); char s[10];
    while(m --) {
        scanf("%s", s);
        if(s[0] == 'A') {
            GET(t1); GET(t2); GET(t3);
            Add(t1, t2, t3);
        }
        else if(s[0] == 'M') {
            GET(t1); GET(t2);
            printf("%d\n", QMIN(t1, t2));
        }
        else if(s[0] == 'I') {
            GET(t1); GET(t2); Ins(t1, t2);
        }
        else if(s[0] == 'D') {
            GET(t1); Del(t1);
        }
        else if(s[3] == 'E') {
            GET(t1); GET(t2);
            Rev(t1, t2);
        }
        else {
            GET(t1); GET(t2); GET(t3);
            Revolve(t1, t2, t3);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值