zoukankan      html  css  js  c++  java
  • POJ3468--A Simple Problem with Integers--线段树/树状数组 改段求段

    题目描述

    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 Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
    "Q a b" means querying the sum of Aa, Aa+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.

    解题思路

    不同于改段求点或改点求段,这是一个改段求段的题目
    有两种方法接题:
    第一种为利用树状数组:

    区间更新这里引进了一个数组delta数组,delta[i]表示区间 [i, n] 的共同增量,每次你需要更新的时候只需要更新delta数组就行了,因为每段区间更新的数都记录在这个数组
    中,那怎么查询前缀和呐?
    sum[i]=a[1]+a[2]+a[3]+......+a[i]+delta[1](i-0)+delta[2](i-1)+delta[3](i-2)+......+delta[i](i-i+1);
    = sigma( a[x] ) + sigma( delta[x] * (i + 1 - x) )
    = sigma( a[x] ) + (i + 1) * sigma( delta[x] ) - sigma( delta[x] * x )

    (引用自吕程博客)
    具体代码为

    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    using namespace std;
    const int M=100010;
    long long add_sum[M];
    long long add[M];
    long long  ori[M];
    int MN;
    int Q;
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int i,int v,long long* a)
    {
        for(;i<=MN;i+=lowbit(i)){
            a[i]+=v;
        }
    }
    long long getsum(int r,long long  *a)
    {
        long long resr=0;
        for(;r>0;r-=lowbit(r)){
            resr+=a[r];
        }
        return resr;
    }
    int main()
    {
        //freopen("data.in","r",stdin);
        std::ios::sync_with_stdio(false);
    	std::cin.tie(0);
    	int tem;
    	int l,r,val;
    	while(cin>>MN>>Q){
            memset(ori,0,sizeof(ori));
            memset(add_sum,0,sizeof(add_sum));
            memset(add,0,sizeof(add));
            for(int i=1;i<=MN;i++){
                cin>>tem;
                update(i,tem,ori);
            }
            string ch;
            for(int i=0;i<Q;i++){
                cin>>ch;
                if(ch=="Q"){
                    cin>>l>>r;
                    cout<<getsum(r,ori)-getsum(l-1,ori)-l*getsum(l-1,add)+(r+1)*getsum(r,add)-getsum(r,add_sum)+getsum(l-1,add_sum)<<endl;
                }
                if(ch=="C"){
                    cin>>l>>r>>val;
                    update(l,val,add);
                    update(r+1,-val,add);
                    update(l,val*(l),add_sum);
                    update(r+1,-val*(r+1),add_sum);
                }
            }
    	}
    }
    

    注意:add_sum数组保存的是add[i]*i之后的值



    第二种方法为利用线段树
    可以写一个利用结构体实现的线段树,然后加懒标。我地AC代码是按照刘汝佳的板写的,我觉得他地方法比较简洁,更好玩
    下面是代码(详解见算法竞赛入门经典-训练指南):

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<stdlib.h>
    using namespace std;
    const int M = 4*100010;
    long long sumv[M];//线段树sum数组
    long long add[M];
    long long  x, y;//全局变量,update与query时使用
    long long  addval;
    long long  _sum;
    //*********************//
    void update(int id, int l, int r)
    {
    	long long m = l + (r - l) / 2;
    	if (x <= l&&y >= r){
    		add[id] += addval;
    	}
    	else{
    		if (x <= m) update(2 * id, l, m);
    		if (y>m) update(2 * id + 1, m+1, r);
    	}
    	sumv[id]=0;//!!!!!!!!!注意此步骤
        if (l<r){
    		sumv[id] = sumv[2 * id] + sumv[2 * id + 1];
    	}
    	sumv[id] += add[id] * (r - l + 1);
    }
    void query(int id, int l, int r, long long addv)
    {
    	long long  m = l + (r - l) / 2;
    	if (x <= l&&y >= r){
    		_sum += sumv[id] + addv*(r - l + 1);
    	}
    	else{
            //addv += add[id];
    		if (x <= m){
    			query(2 * id, l, m, addv+add[id]);
    		}
    		if (y>m) query(2 * id + 1, m+1, r, addv+add[id]);
    	}
    }
    int main()
    {
    	//freopen("data.in", "r", stdin);
    	//freopen("data.out","w",stdout);
    	std::ios::sync_with_stdio(false);
    	std::cin.tie(0);
    	long long  n, q;
    	while (cin >> n >> q){
    		memset(sumv, 0, sizeof(sumv));
    		memset(add, 0, sizeof(add));
    		for (int i = 1; i <= n; i++){
    			cin >> addval;
    			x = i;
    			y = i;
    			update(1, 1, n);
    		}
    		string ch;
    		for (int i = 0; i<q; i++){
    			cin >> ch;
    			if (ch == "Q"){
    				_sum = 0;
    				cin >> x >> y;
    				query(1, 1, n, 0);
    				cout << _sum << endl;
    			}
    			if (ch == "C"){
    				cin >> x >> y >> addval;
    				update(1, 1, n);
    
    			}
    		}
    	}
    }
    
  • 相关阅读:
    联考20200801 T2 皮卡丘
    联考20200729 T2 划愤
    联考20200801 T1 林海的密码
    联考20200725 T2 Tree
    联考20200721 T1 s1mple
    联考20200730 T2 小B的环
    联考20200730 T1 小B的班级
    联考20200718 T1 因懒无名
    联考20200723 T1 数
    联考20200722 T1 集合划分
  • 原文地址:https://www.cnblogs.com/liuzhanshan/p/5894121.html
Copyright © 2011-2022 走看看