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

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 线段树的 点更新 区间求和

时间: 2024-10-06 21:24:26

HDU 4893 线段树的 点更新 区间求和的相关文章

hdu 1166 敌兵布阵(线段树之 单点更新+区间求和)

敌兵布阵                                                                             Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵

hdu 1166 线段树(单点增减 区间求和)

Sample Input1101 2 3 4 5 6 7 8 9 10Query 1 3Add 3 6Query 2 7Sub 10 2Add 6 3Query 3 10End Sample OutputCase 1:63359 1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <cmath>

(线段树成段更新+区间求和) poj 3468

D - A Simple Problem with Integers Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 3468 Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One typ

hdu 4893 线段树 --- 也是两个变 类似双标记

http://acm.hdu.edu.cn/showproblem.php?pid=4893 开始的时候,我按双标记,WA了一下午,搞不定,我是用的两个标记add--表示当前结点中有值发生变化,flag,斐波那契的懒惰标记,但是估计是我自己处理的有问题,一直不对 参考了别人的代码,写法还是很不错的,Add变量维护的是,完全变成Fibonacci的时候的和,---回头我再重新写一遍 #include <cstdio> #include <cstring> #include <a

HDU 4893 线段树裸题

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2512    Accepted Submission(s): 751 Problem Description Recently, Doge got a funny birthday present from his new friend, Pro

POJ训练计划2777_Count Color(线段树/成段更新/区间染色)

解题报告 题意: 对线段染色,询问线段区间的颜色种数. 思路: 本来直接在线段树上染色,lz标记颜色.每次查询的话访问线段树,求出颜色种数.结果超时了,最坏的情况下,染色可以染到叶子节点. 换成存下区间的颜色种数,这样每次查询就不用找到叶子节点了,用按位或来处理颜色种数. #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace

hdu4027 Can you answer these queries?(线段树平方减少,区间求和)

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4027 Problem Description A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the bat

HDU 3397 Sequence operation(线段树&#183;成段更新&#183;区间合并&#183;混合操作)

题意  给你一个只有0, 1的数组  有这些操作 0. 将[a, b]区间的所有数都改为0 1. 将[a, b]区间的所有数都改为1 2. 将[a, b]区间的所有数都取反 即与1异或 3. 输出区间[a, b]中1的个数  即所有数的和 4. 输出区间[a, b]中最大连续1的长度 对于所有的3, 4操作输出对应的答案 单个的操作都很简单  但搞在一起就有点恶心了  还好数组里的数只有0和1 线段树维护9个值 对应区间0, 1的最大长度len[i]  对应区间左端点为起点的最大0, 1长度ll

hdu 1754 I Hate It(线段树之 单点更新+区间最值)

I Hate It                                                                             Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Description 非常多学校流行一种比較的习惯.老师们非常喜欢询问.从某某到某某其中,分数最高的是多少. 这让非常多学生非常反感. 无论你