poj 3468 A Simple Problem with Integers - Splay

题目链接: http://poj.org/problem?id=3468

题意:区间更新,求区间和。


本来是线段树区间更新的入门题,一直听说Splay这个神奇的数据结构没去学,于是就学了一下,第一道。。

推荐学习:

http://notonlysuccess.me/?p=709

http://blog.youkuaiyun.com/acm_cxlove/article/details/7815019


先主要搞懂两种旋转方式,实现就是Rotate和Splay两个操作,这几基本一样没什么变化。

主要变化就是区间操作的时候,要加入lazy标记,然后添加了pushUp和pushDown两个操作。


#include <iostream>
#include <cstdio>
using namespace std;
#define Key_value (ch[ch[root][1]][0])

typedef long long ll;
const int maxn = 1e5+10;
int n, q, a[maxn];

// splay
int tot1, root;
int sz[maxn], pre[maxn], ch[maxn][2];
ll add[maxn], sum[maxn], val[maxn];

//debug部分copy from hh
void Treaval(int x) {
    if(x) {
        Treaval(ch[x][0]);
        printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d ,val = %2d , sum = %2d \n",x,ch[x][0],ch[x][1],pre[x],sz[x],val[x],sum[x]);
        Treaval(ch[x][1]);
    }
}
void debug() {printf("%d\n",root);Treaval(root);}

void newNode(int &r, int father, int k)
{
    r = ++tot1;
    pre[r] = father;
    sz[r] = 1;
    val[r] = k;
    sum[r] = k;
    add[r] = 0;
    ch[r][0]= ch[r][1] = 0;
}

void pushUp(int r)
{
    sz[r] = sz[ch[r][0]] + sz[ch[r][1]] + 1;
    sum[r] = sum[ch[r][0]] + sum[ch[r][1]] + val[r] + add[r];
}

void pushDown(int r)
{
    if(add[r])
    {
        val[r] += add[r];

        add[ch[r][0]] += add[r];
        add[ch[r][1]] += add[r];

        sum[ch[r][0]] += 1LL*add[r]*sz[ch[r][0]];
        sum[ch[r][1]] += 1LL*add[r]*sz[ch[r][1]];

        add[r] = 0;
    }
}

void build(int &x, int l, int r, int father)
{
    if(l > r) return;
    int mid = (l + r) >> 1;
    newNode(x, father, a[mid]);
    if(l < mid) build(ch[x][0], l, mid-1, x);
    if(r > mid) build(ch[x][1], mid+1, r, x);
    pushUp(x);
}

// kind=0左旋, kind=1右旋
void Rotate(int x, int kind)
{
    int y = pre[x];
    pushDown(x);
    pushDown(y);
    //
    ch[y][!kind] = ch[x][kind];
    pre[ch[x][kind]] = y;

    if(pre[y]) ch[pre[y]][y == ch[pre[y]][1]] = x;

    pre[x] = pre[y];
    pre[y] = x;
    ch[x][kind] = y;
    pushUp(y);
}

void Splay(int r, int goal)
{
    pushDown(r);
    while(pre[r] != goal)
    {
        if(pre[pre[r]] == goal) Rotate(r, r == ch[pre[r]][0]);
        else
        {
            int y = pre[r];
            int kind = ch[pre[y]][0] == y;
            // 方向不同
            if(ch[y][kind] == r)
            {
                Rotate(r, !kind);
                Rotate(r, kind);
            }
            // 方向相同,相同方向转两次
            else
            {
                Rotate(y, kind);
                Rotate(r, kind);
            }
        }
    }
    pushUp(r);
    if(goal == 0) root = r;
}

// 把第k位的数转到goal下面
void RotateTo(int k, int goal)
{
    int r = root;
    pushDown(r);
    while(sz[ch[r][0]] != k)
    {
        if(k < sz[ch[r][0]]) r = ch[r][0];
        else
        {
            k -= ( sz[ch[r][0]] + 1);
            r = ch[r][1];
        }
        pushDown(r);
    }
    Splay(r, goal);
}

ll query(int l, int r)
{
    RotateTo(l-1, 0);
    //debug();
    RotateTo(r+1, root);
    //debug();
    return sum[Key_value];
}

void update(int l, int r, int c)
{
    RotateTo(l-1, 0);
    RotateTo(r+1, root);
    add[Key_value] += c;
    sum[Key_value] += 1LL*sz[Key_value] * c;
}

void init()
{
    for(int i=0;i<n;i++) scanf("%d", &a[i]);
    sz[0] = ch[0][0] = ch[0][1] = pre[0] = 0;
    add[0] = sum[0] = 0;
    root = tot1 = 0;
    newNode(root, 0, -1);
    newNode(ch[root][1], root, -1);
    sz[root] = 2;
    build(Key_value, 0 , n-1, ch[root][1]);
    pushUp(ch[root][1]);
    pushUp(root);
}

int main()
{
    while(~scanf("%d%d", &n, &q))
    {
        init();
        //debug();
        while(q--)
        {
            char buf[10];
            int x, y, c;
            scanf("%s%d%d", buf, &x, &y);
            if(buf[0] == 'Q') printf("%lld\n", query(x, y));
            else
            {
                scanf("%d", &c);
                update(x, y, c);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值