码迷,mamicode.com
首页 > 编程语言 > 详细

poj3468(A Simple Problem with Integers)线段树+树状数组

时间:2015-08-17 15:41:52      阅读:147      评论:0      收藏:0      [点我收藏+]

标签:acm   线段树   树状数组   

Description

You have N integers, A1A2, ... , 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 A1A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C abc" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q ab" 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.

题意:给你n个整数和q个操作,有两种操作,C l r x表示把[l, r]中所有数都加x,Q l r表示输出[l, r]中数值的和。


用线段树和树状数组都能做。


方法一:线段树。因为操作一需要修改一个区间内所有的值,所以最坏情况下需要修改整个线段树,复杂度太高。所以可以用线段树来维护两个值,一个是区间共同加上的值,一个是除了这些共同值以外其他的值的和。


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <map>
#include <vector>
#include <list>
#include <set>
#include <stack>
#include <queue>
#include <deque>
#include <algorithm>
#include <functional>
#include <iomanip>
#include <limits>
#include <new>
#include <utility>
#include <iterator>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <ctime>
using namespace std;
typedef long long ll;

const int maxn = (1 << 18) - 1;
int n, q;

//data维护区间共同值,datb维护除了这些共同值以外的值的和
ll data[maxn], datb[maxn];

//对区间[a, b)同时加x
//k是节点的编号,对应的区间是[l, r)
void add(int a, int b, int x, int k, int l, int r)
{
    if (a <= l && b >= r)
        data[k] += x;
    else
        if (b > l && a < r)
        {
            datb[k] += (min(b, r) - max(a, l)) * x;
            add(a, b, x, k*2+1, l, (l+r)/2);
            add(a, b, x, k*2+2, (l+r)/2, r);
        }
}

//计算[a, b)的和
//k是节点的编号,对应的区间是[l, r)
ll sum(int a, int b, int k, int l, int r)
{
    if (b <= l || a >= r)
        return 0;
    if (a <= l && b >= r)
        return (r - l) * data[k] + datb[k];
    ll res = (min(b, r) - max(a, l)) * data[k];
    res += sum(a, b, k*2+1, l, (l+r)/2);
    res += sum(a, b, k*2+2, (l+r)/2, r);
    return res;
}

int main()
{
    cin >> n >> q;
    for (int i = 0; i < n; ++i)
    {
        int num;
        scanf("%d", &num);
        add(i, i+1, num, 0, 0, n);
    }
    while (q--)
    {
        char s[5];
        int l, r, x;
        scanf("%s", s);
        if (s[0] == 'C')
        {
            scanf("%d%d%d", &l, &r, &x);
            add(l-1, r, x, 0, 0, n);
        }
        else
        {
            scanf("%d%d", &l, &r);
            printf("%lld\n", sum(l-1, r, 0, 0, n));
        }
    }
    return 0;
}


方法二:树状数组。类似地,构建两个树状数组即可。


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <map>
#include <vector>
#include <list>
#include <set>
#include <stack>
#include <queue>
#include <deque>
#include <algorithm>
#include <functional>
#include <iomanip>
#include <limits>
#include <new>
#include <utility>
#include <iterator>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <ctime>
using namespace std;
typedef long long ll;

const int maxn = 100010;
int n, q;

//BIT
ll bit0[maxn], bit1[maxn];

ll sum(ll* b, int i)
{
    ll s = 0;
    while (i > 0)
    {
        s += b[i];
        i &= i - 1;
    }
    return s;
}

void add(ll* b, int i, int x)
{
    while (i <= n)
    {
        b[i] += x;
        i += i & (-i);
    }
}

int main()
{
    cin >> n >> q;
    for (int i = 1; i <= n; ++i)
    {
        int num;
        scanf("%d", &num);
        add(bit0, i, num);
    }
    while (q--)
    {
        char s[5];
        int l, r, x;
        scanf("%s", s);
        if (s[0] == 'C')
        {
            scanf("%d%d%d", &l, &r, &x);
            add(bit0, l, -x*(l-1));
            add(bit1, l, x);
            add(bit0, r+1, x*r);
            add(bit1, r+1, -x);
        }
        else
        {
            scanf("%d%d", &l, &r);
            ll res = 0;
            res += sum(bit0, r) + sum(bit1, r) * r;
            res -= sum(bit0, l-1) + sum(bit1, l-1) * (l - 1);
            printf("%lld\n", res);
        }
    }
    return 0;
}




版权声明:本文为博主原创文章,转载请注明出处。

poj3468(A Simple Problem with Integers)线段树+树状数组

标签:acm   线段树   树状数组   

原文地址:http://blog.csdn.net/god_weiyang/article/details/47724499

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!