codevs 1081 线段树练习2(差分树状数组)

1081 线段树练习 2

 

时间限制: 1 s
空间限制: 128000 KB
题目等级 : 大师 Master
 
 
 
题目描述 Description

给你N个数,有两种操作


1:给区间[a,b]的所有数都增加X


2:询问第i个数是什么?

输入描述 Input Description

第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,表示操作的个数. 接下来Q行每行若干个整数。如果第一个数是1,后接3个正整数a,b,X,表示在区间[a,b]内每个数增加X,如果是2,后面跟1个整数i, 表示询问第i个位置的数是多少。

输出描述 Output Description

对于每个询问输出一行一个答案

样例输入 Sample Input

3

1

2

3

2

1 2 3 2

2 3

样例输出 Sample Output

5

数据范围及提示 Data Size & Hint

数据范围

1<=n<=100000

1<=q<=100000

 

一般的树状数组是 单点更新,区间查询。

这里如果我们把每个数和它前一个数的差值来建立树状数组的话。

那么某位置原本所代表的数,就是从开始到这个位置的区间和。

比如 1 2 4,建立树状数组是1 1 2,第三个数通过1+1+2得到。

这个时候只需要更新端点位置的值就可以达到目的,因为差值具有传递效应

 现在树状数组是1 1 2,如果我们需要将[1,2]这个区间进行+2操作

那么将树状数组变为 3 1 0,即可。

将区间[l,r]加上x的话

只需要add(l,x) add(r+1,-x)

 

代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN=1e5+10;
LL n,m;
LL c[MAXN];
LL a[MAXN];
LL lowbit(LL x){return x&-x;}
void add(LL x,LL val)
{
    for(int i=x;i<=n;i+=lowbit((i)))
      c[i]+=val;
}

LL get(LL x)
{
  LL ans=0;
  for(int i=x;i>=1;i-=lowbit(i))
  ans+=c[i];
  return ans;
}
int main()
{
    LL op,c,d;
    LL val;
    scanf("%lld",&n);
    a[0]=0;
    for(int i=1;i<=n;i++)
    {
    scanf("%lld",&a[i]);
    add(i,a[i]-a[i-1]);
    }
    scanf("%lld",&m);
    while(m--)
    {
        scanf("%lld",&op);
      if(op==1)
      {
          scanf("%lld%lld%lld",&c,&d,&val);
          add(c,val);
          add(d+1,-val);
      }
      else
      {
          scanf("%lld",&c);
          printf("%lld\n",get(c));
      }
    }

    return 0;
}

 

 

posted @ 2018-03-16 11:16  hinata_hajime  阅读(131)  评论(0编辑  收藏  举报