伸展树(splay)入门学习(三) POJ3468 A Simple Problem with Integers

A Simple Problem with Integers

Time Limit: 5000MS   Memory Limit: 131072K
Total Submissions: 139870   Accepted: 43323
Case Time Limit: 2000MS

Description

You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of AaAa+1, ... , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.

这个用线段树很好写,用伸展树写有一点麻烦,但思想是一样的,用个lazy标记就行了,注意打上标记以后要先改子区间的值,不然会wa

#include <algorithm>
#include <cstdio>
#include <iostream>
typedef long long ll;
using namespace std;
ll n;
ll sum[100005];
const ll inf = (ll)1e15;
struct tree
{
    ll father,lch,rch,size,val; //父节点、左儿子、右儿子、子树大小、值
}stree[100005];
ll lazy[100005]; // lazy标记
ll root; // 根节点,树的大小
ll a[100005];
inline bool get(ll x) // 判断是父亲的左儿子还是右儿子
{
    return stree[ stree[x].father ].rch == x;
}
inline void update(ll x) // 更新子树大小
{
    stree[x].size = stree[ stree[x].lch ].size + stree[ stree[x].rch ].size + 1;
    sum[x] = sum[stree[x].lch] + sum[stree[x].rch] + stree[x].val;
}
void pushdown(ll x)
{
    if (lazy[x])  // 如果当前节点被标记过
    {
        ll lch = stree[x].lch;
        ll rch = stree[x].rch;
        stree[lch].val += lazy[x];
        stree[rch].val += lazy[x];
        sum[rch] += lazy[x] * stree[rch].size;
        sum[lch] += lazy[x] * stree[lch].size;//更新区间和
        lazy[lch] += lazy[x];
        lazy[rch] += lazy[x]; //标记左右儿子节点
        lazy[x] = 0; //取消当前节点的标记
    }
}
ll build(ll l, ll r, ll rt)
{
    if (l > r)
        return 0;
    ll mid = (l + r) / 2;
    ll lch = build(l, mid - 1, mid);   //创建左子树
    ll rch = build(mid + 1, r, mid);   //创建右子树
    stree[mid].father = rt;
    stree[mid].val = a[mid];
    stree[mid].size = 1;
    stree[mid].lch = lch;
    stree[mid].rch = rch;    // 创建该节点
    update(mid);  // 更新mid节点
    return mid;
}
inline void rotate(ll x)//旋转
{
    pushdown( stree[x].father );  // 更新x的父节点的lazy标记
    pushdown(x);  // 更新x的lazy标记
    ll fa,oldf,whichx;
    fa = stree[x].father;  // x的父节点
    oldf = stree[fa].father;  // x父节点的父节点
    whichx = get(x); // 判断x是父节点左儿子还是右儿子
    if (whichx) //x是父节点的右儿子
    {
        stree[fa].rch = stree[x].lch;
        stree[ stree[fa].rch ].father = fa; // 将x的左儿子变为x父节点的右儿子
        stree[x].lch = fa;
        stree[fa].father = x;
        stree[x].father = oldf; // 将x父节点变为x左儿子
    }
    else    // 同上
    {
        stree[fa].lch = stree[x].rch;
        stree[ stree[fa].lch ].father = fa;
        stree[x].rch = fa;
        stree[fa].father = x;
        stree[x].father = oldf;
    }
    if (oldf) // 如果有x的父节点有父节点,则更新祖父节点的(左/右)儿子为x
    {
        if (stree[oldf].rch == fa)
            stree[oldf].rch = x;
        else
            stree[oldf].lch = x;
    }
    update(x);
    update(fa);// 更新x与x父节点的大小
}
inline void splay(ll x, ll t)//将x伸展到t的子节点
{
    pushdown(x);
    ll fa = stree[x].father; //将x伸展至父节点
    while (fa != t)
    {
        if (stree[fa].father != t) // 如果父节点不是目的节点
        {
            if (get(x) == get(fa)) // 一字型(需旋转父节点再旋转x)
                rotate(fa);
            else
                rotate(x); // 之字形(需旋转x两次)
        }
        rotate(x); // 旋转x
        fa = stree[x].father; //更新父节点
    }
    if (!t)
        root = x; // 更新根节点
}
ll find(ll x) //查找x为第几小的值
{
    ll u = root;
    while (1)
    {
        if (x <= stree[stree[u].lch].size) // 如果x在当前节点本身或者当前节点的左子树内
            u = stree[u].lch;
        else // 如果x在当前节点的右子树内
        {
            x -= stree[stree[u].lch].size;
            if (x == 1) // 恰好是当前节点
                return u;
            x--;  // 减去当前节点的出现次数
            u = stree[u].rch;  //  更新当前节点
        }
    }
}
void init()
{
    a[1] = a[n + 2] = inf; // 两端的标记
    root = build(1, n + 2, 0); //建树
}
void adddd(ll l, ll r, ll ad) // 加
{
    ll x = find(l);
    ll y = find(r + 2);
    splay(x, 0);
    splay(y, x);
    lazy[stree[y].lch] += ad;
    sum[stree[y].lch] += ad * stree[stree[y].lch].size;
    stree[stree[y].lch].val += ad;
}
ll querysum(ll l, ll r) //求和
{
    ll x = find(l);
    ll y = find(r + 2);
    splay(x, 0);
    splay(y, x);
    return sum[stree[y].lch];
}
int main()
{
//    freopen("in.txt", "r", stdin);
    ll q,l,r;
    ll cnt;
    char c;
    scanf("%lld%lld", &n, &q);
    for (ll i = 2; i <= n + 1; i++)
        scanf("%lld", &a[i]);
    init();
    while (q--)
    {
        scanf(" %c", &c);
        if (c == 'C')
        {
            scanf("%lld %lld %lld", &l, &r, &cnt);
            adddd(l, r, cnt);
        }
        else
        {
            scanf("%lld %lld", &l, &r);
            printf("%lld\n", querysum(l, r));
        }
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章