首页 > 其他 > 详细

HDU 4893 线段树的 点更新 区间求和

时间:2014-07-31 12:38:26      阅读:317      评论:0      收藏:0      [点我收藏+]

Wow! Such Sequence!

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2067    Accepted Submission(s): 619


Problem Description
Recently, Doge got a funny birthday present from his new friend, Protein Tiger from St. Beeze College. No, not cactuses. It‘s a mysterious blackbox.

After some research, Doge found that the box is maintaining a sequence an of n numbers internally, initially all numbers are zero, and there are THREE "operations":

1.Add d to the k-th number of the sequence.
2.Query the sum of ai where l ≤ i ≤ r.
3.Change ai to the nearest Fibonacci number, where l ≤ i ≤ r.
4.Play sound "Chee-rio!", a bit useless.

Let F0 = 1,F1 = 1,Fibonacci number Fn is defined as Fn = Fn - 1 + Fn - 2 for n ≥ 2.

Nearest Fibonacci number of number x means the smallest Fn where |Fn - x| is also smallest.

Doge doesn‘t believe the machine could respond each request in less than 10ms. Help Doge figure out the reason.
 

 

Input
Input contains several test cases, please process till EOF.
For each test case, there will be one line containing two integers n, m.
Next m lines, each line indicates a query:

1 k d - "add"
2 l r - "query sum"
3 l r - "change to nearest Fibonacci"

1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000, |d| < 231, all queries will be valid.
 

 

Output
For each Type 2 ("query sum") operation, output one line containing an integer represent the answer of this query.
 

 

Sample Input
1 1 2 1 1 5 4 1 1 7 1 3 17 3 2 4 2 1 5
 

 

Sample Output
0 22
 
 

题意:

题目中说对一个长度为n,初始元素都为0的数组进行三种操作,如下:

1 k d   第 k 个元素加上 d
2 l r     求从 l 到 r 所有元素的和
3 l r     改变从 l 到 r 所有元素,都为原元素最近的 Fibonacci 数,差值相等时取较小值

思路:

对于第一个操作需要用到线段树中的单点更新操作,对于第二个操作需要用到线段树中的区 间求和操作,对于第三个操作需要思考一下,怎么处理才能最快地改变我们需要改变区间的状态。因为对于区间有个求和操作,那么我们会考虑到只需要改变一段区 间的和即可。处理的方案就是提前对每一段的 区间和 都找到相应的 Fibonacci 数作为映射,那么我们要对区间进行第三操作时,只需要将区间做一下标记,然后将这个映射值覆盖到原 区间和 即可。

注意点:

1.注意 pushdown 和 pushup 的使用。

2.注意当访问到叶子节点时最好是返回(return),若不返回那么开大线段树的大小(原为4倍)也行。

3.注意杭电的输出为 %I64。
 
#include <cstdio>
#include <cmath>
using namespace std;
#define N 100010

struct node {
    int L, R;
    long long sum, vsum;
    int flag;
} tree[N << 2];
long long ans;

long long ffib(long long val) {
    long long x = 0, y = 1;
    int i;
    for (i = 0; i < 100; ++i) {
        y = x + y;
        x = y - x;
        if(y >= val)
            break;
    }
    if(fabs(y - val) < fabs(x - val))
        return y;
    return x;
}

void pushUp(int root) {    ///向上回溯  不断更新区间和
    if(tree[root].L == tree[root].R) return;
    tree[root].sum = tree[root << 1].sum + tree[root << 1 | 1].sum;
    tree[root].vsum = tree[root << 1].vsum + tree[root << 1 | 1].vsum;
}

void pushDown(int root) {   ///向下更新   falg==1更新的时候
    if(tree[root].flag && tree[root].L == tree[root].R) {
        tree[root].sum = tree[root].vsum;   ///.vsum 求和之后对应该点的Fri数
        tree[root].flag = 0;   ///遍历完了
        return ;
    }
    if(tree[root].flag) {  
        tree[root << 1].flag = tree[root << 1 | 1].flag = 1;
        tree[root << 1].sum = tree[root << 1].vsum;
        tree[root << 1 | 1].sum = tree[root << 1 | 1].vsum;
        tree[root].flag = 0;   ///遍历完了
    }
}

void build(int L, int R, int root) {   ///建树
    tree[root].L = L;
    tree[root].R = R;
    tree[root].flag = 0;
    if(L == R) {
        tree[root].sum = 0;
        tree[root].vsum = 1;
        return ;
    }
    int mid = (L + R) >> 1;
    build(L, mid, root << 1);
    build(mid + 1, R, root << 1 | 1);
    pushUp(root);    ///递归遍历求和
}

void add(int pos, int val, int root) {
    if (tree[root].L == tree[root].R) {
        tree[root].sum += val;
        tree[root].vsum = ffib(tree[root].sum);
        tree[root].flag = 0;
        return ;
    }
    pushDown(root);
    int mid = (tree[root].L + tree[root].R) >> 1;
    if (mid >= pos) add(pos, val, root << 1);
    else add(pos, val, root << 1 | 1);
    pushUp(root);
}

void update(int L, int R, int root) {
    if (L <= tree[root].L && R >= tree[root].R) {
        tree[root].flag = 1;
        tree[root].sum = tree[root].vsum;
        return ;
    }
    pushDown(root);
    int mid = (tree[root].L + tree[root].R) >> 1;
    if (mid >= R) update(L, R, root << 1);
    else if (mid + 1 <= L) update(L, R, root << 1 | 1);
    else {
        update(L, mid, root << 1);
        update(mid + 1, R, root << 1 | 1);
    }
    pushUp(root);
}

void query(int L, int R, int root) {
    if (L <= tree[root].L && R >= tree[root].R) {
        ans += tree[root].sum;
        return ;
    }
    pushDown(root);
    int mid = (tree[root].L + tree[root].R) >> 1;
    if (mid >= R) query(L, R, root << 1);
    else if (mid + 1 <= L) query(L, R, root << 1 | 1);
    else {
        query(L, mid, root << 1);
        query(mid + 1, R, root << 1 | 1);
    }
    pushUp(root);
}

int main(int argc, char *argv[]) {
    int n, m, i;
    int op, x, y;
    while(scanf("%d%d", &n, &m) != EOF) {
        build(1, n, 1);
        for (i = 0; i < m; ++i) {
            scanf("%d%d%d", &op, &x, &y);
            if(op == 1) {
                add(x, y, 1);
            } else if(op == 2) {
                ans = 0;
                query(x, y, 1);
                printf("%I64d\n", ans);  // hdu
            } else if(op == 3) {
                update(x, y, 1);
            }
        }
    }
    return 0;
}

 

HDU 4893 线段树的 点更新 区间求和,布布扣,bubuko.com

HDU 4893 线段树的 点更新 区间求和

原文:http://www.cnblogs.com/zhangying/p/3880206.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!