poj 3468 A Simple Problem with Integers(原来是一道简单的线段树区间修改用来练练splay)

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

题解:splay功能比线段树强大当然代价就是有些操作比线段树慢,这题用splay实现的比线段树慢上一倍。线段树用lazy标记差不多要2s用splay要4s。可以用splay来实现线段树的区间操作更深层次的了解一下splay算是入个门。

#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cstdio>
using namespace std;
const int M = 1e5 + 10;
typedef long long ll;
int pre[M] , ch[M][2] , size[M] , root , tot;//分别表示父节点,左右儿子,大小,根节点,总共的节点数。
int key[M];//当前节点对应的权值
int add[M];//类似懒惰标记
ll sum[M];//当前节点包括他以下的节点权值的总和可以理解为子树权值之和加上这个节点的权值
int a[M];
int n , q;
void NewNode(int &r , int fa , int k) {
    r = ++tot;
    pre[r] = fa;
    size[r] = 1;
    key[r] = k;
    add[r] = 0;
    sum[r] = 0;
    ch[r][0] = ch[r][0] = 0;
}//标准的初始化节点
void update(int r , int ad) {
    if(r == 0) return;
    add[r] += ad;
    key[r] += ad;
    sum[r] += (ll)ad * size[r];
}//节点更新
void push_up(int r) {
    size[r] = size[ch[r][0]] + size[ch[r][1]] + 1;
    sum[r] = sum[ch[r][0]] + sum[ch[r][1]] + key[r];
}
void push_down(int r) {
    if(add[r]) {
        update(ch[r][0] , add[r]);
        update(ch[r][1] , add[r]);
        add[r] = 0;
    }
}//一系列类似线段树的操作。
void Rotate(int x , int kind) {
    int y = pre[x];
    push_down(y);
    push_down(x);
    ch[y][!kind] = ch[x][kind];
    pre[ch[x][kind]] = y;
    if(pre[y]) ch[pre[y]][ch[pre[y]][1] == y] = x;
    pre[x] = pre[y];
    ch[x][kind] = y;
    pre[y] = x;
    push_up(y);
}
void Splay(int r , int goal) {
    push_down(r);
    while(pre[r] != goal) {
        if(pre[pre[r]] == goal) Rotate(r , ch[pre[r]][0] == r);
        else {
            int y = pre[r];
            int kind = (ch[pre[y]][0] == y);
            if(ch[y][kind] == y) {
                Rotate(r , !kind);
                Rotate(r , kind);
            }
            else {
                Rotate(y , kind);
                Rotate(r , kind);
            }
        }
    }
    push_up(r);
    if(goal == 0) root = r;
}//一系列标准的splay的操作
void build(int &x , int l , int r , int fa) {
    if(l > r) return ;
    int mid = (l + r) >> 1;
    NewNode(x , fa , a[mid]);
    build(ch[x][0] , l , mid - 1 , x);
    build(ch[x][1] , mid + 1 , r , x);
    push_up(x);
}
void init() {
    root = 0 , tot = 0;
    ch[root][0] = ch[root][1] = pre[root] = size[root] = add[root] = sum[root] = key[root] = 0;
    NewNode(root , 0 , -1);
    NewNode(ch[root][1] , root , -1);
    build(ch[ch[root][1]][0] , 1 , n , ch[root][1]);
    push_up(root);
    push_up(ch[root][1]);
}//这里之所以要优先建两个点和后面的更新有关
int get_kth(int r , int k) {
    push_down(r);
    int t = size[ch[r][0]] + 1;
    if(t == k) return r;
    else if(t > k) return get_kth(ch[r][0] , k);
    else return get_kth(ch[r][1] , k - t);
}//获得第几大的数
void ADD(int l , int r , int ad) {
    Splay(get_kth(root , l) , 0);
    Splay(get_kth(root , r + 2) , root);
    update(ch[ch[root][1]][0] , ad);
    push_up(ch[root][1]);
    push_up(root);
}//这里按照常理应该是将第l-1个节点移到根然后再将r+1的节点移到根的右儿子那么(l~r)就是r+1节点的左儿子的sum值由于之前加了两个节点所以变到了l~r+2,毕竟l-1可能为0就是就是根节点处理起来可能会有些不便。当然无视也行,按照个人喜好来就行。
long long query(int l , int r) {
    Splay(get_kth(root , l) , 0);
    Splay(get_kth(root , r + 2), root);
    return sum[ch[ch[root][1]][0]];
}//区间查询同理
int main() {
    while(scanf("%d%d" , &n , &q) == 2) {
        for(int i = 1 ; i <= n ; i++) scanf("%d" , &a[i]);
        init();
        while(q--) {
            char c[10];
            scanf("%s" , c);
            if(c[0] == ‘Q‘) {
                int l , r;
                scanf("%d%d" , &l , &r);
                printf("%lld\n" , query(l , r));
            }
            else {
                int l , r , x;
                scanf("%d%d%d" , &l , &r , &x);
                ADD(l , r , x);
            }
        }
    }
    return 0;
}
时间: 2024-10-13 10:23:58

poj 3468 A Simple Problem with Integers(原来是一道简单的线段树区间修改用来练练splay)的相关文章

POJ 3468 A Simple Problem with Integers(线段树)

题目链接:http://poj.org/problem?id=3468 A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 56005   Accepted: 16903 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with

poj 3468 A Simple Problem with Integers 【线段树-成段更新】

题目:poj 3468 A Simple Problem with Integers 题意:给出n个数,两种操作 1:l -- r 上的所有值加一个值val 2:求l---r 区间上的和 分析:线段树成段更新,成段求和 树中的每个点设两个变量sum 和 num ,分别保存区间 l--r 的和 和l---r 每个值要加的值 对于更新操作:对于要更新到的区间上面的区间,直接进行操作 加上 (r - l +1)* val .下面的区间标记num += val 对于求和操作,每次进行延迟更新,把num值

POJ 3468 A Simple Problem with Integers(线段树区间更新)

题目地址:POJ 3468 打了个篮球回来果然神经有点冲动..无脑的狂交了8次WA..居然是更新的时候把r-l写成了l-r... 这题就是区间更新裸题.区间更新就是加一个lazy标记,延迟标记,只有向下查询的时候才将lazy标记向下更新.其他的均按线段树的来就行. 代码如下: #include <iostream> #include <cstdio> #include <cstring> #include <math.h> #include <stac

POJ 3468 A Simple Problem with Integers 【树状数组】

题目链接:http://poj.org/problem?id=3468 题目大意:给出一组数组v[i],有两种操作,一种给出两个数a,b,要求输出v[a]到v[b]之间的和,另一种给出三个数a,b,c,让v[a]到v[b]之间的数全都加上c. 完全是树状数组能够实现的功能,但是如果就这样单纯的套用模板,做第二种操作是更新每个值,这样的操作就有可能超时. 换一种思路,既然第二种操作是给某区间上的所有数加上相同的值,那么应该是能够简化的才对. 假设数组sum[i]为原数组从v[1]到v[i]的和,数

POJ 3468 A Simple Problem with Integers(详细题解)

这是个线段树题目,做之前必须要有些线段树基础才行不然你是很难理解的. 此题的难点就是在于你加的数要怎么加,加入你一直加到叶子节点的话,复杂度势必会很高的 具体思路 在增加时,如果要加的区间正好覆盖一个节点,则增加其节点的Inc值,不再往下走,否则要更新Sum(加上本次增量),再将增量往下传. 这样更新的复杂度就是O(log(n))在查询时,如果待查区间不是正好覆盖一个节点,就将节点的Inc往下带,然后将Inc代表的所有增量累加到Sum上后将Inc清0,接下来再往下查询. Inc往下带的过程也是区

poj 3468:A Simple Problem with Integers(线段树,区间修改求和)

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 58269   Accepted: 17753 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of

线段树(成段更新) POJ 3468 A Simple Problem with Integers

题目传送门 1 /* 2 线段树-成段更新:裸题,成段增减,区间求和 3 注意:开long long:) 4 */ 5 #include <cstdio> 6 #include <iostream> 7 #include <algorithm> 8 #include <cstring> 9 #include <cmath> 10 using namespace std; 11 12 #define lson l, mid, rt <<

POJ - 3468 A Simple Problem with Integers (区间求和)

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. In

poj 3468 A Simple Problem with Integers (线段树 成段更新 加值 求和)

题目链接 题意: 只有这两种操作 C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000."Q a b" means querying the sum of Aa, Aa+1, ... , Ab. 分析:自己写的有点麻烦了,写的时候手残+脑残,改了好久. val+lazy*(r-l+1)表示和,如果lazy==0表示当前区间加的值不统一. 1 #include <iostream