伸展樹(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;
}

 

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