洛谷 P3368 树状数组 2

传送门


题目描述

如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数数加上x
2.求出某一个数的和

输入输出格式

输入格式:
第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含2或4个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k
操作2: 格式:2 x 含义:输出第x个数的值

输出格式:
输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入样例:
5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

输出样例:
6
10

说明

时空限制:1000 ms,128 M
数据规模:
对于30%的数据:N<=8,M<=10
对于70%的数据:N<=10000,M<=10000
对于100%的数据:N<=500000,M<=500000

样例说明:


莫名觉得好麻烦啊……我还是更喜欢线段树虽然它长不过还是要好好学学的呢

模板题,改点求段看前文嘻嘻
一. 区间修改
在用树状数组实现改段求点时,我们还要用到差分数组,那么差分是什么???
首先我们有一个数列 a[ ]
1 5 4 2 3
然后我们又有一个差分数组 b[ ](b[i]=a[i]-a[i-1])
1 4 -1 -2 1
现在我们使区间[2,4]内的数加上二
则b数列变为
1 6 -1 -2 -1
我们发现变化的只有b[2]与b[5],b[3]和b[4]是不变的,b[2]加上了2,b[4]减去了2。
因此我们可以推出
对于[x,y]区间的修改(增加的值为c),我们只需令b[x]+c,b[y+1]-c。

二. 单点查询
我们令a[0]=0,则b[1]=a[1]。
因此当我们要查询a[k]的值时,我们只需计算 ki=1b[i] ∑ i = 1 k b [ i ]
b[1]+b[2]+b[3]+…+b[k] = a[1]-a[1]+a[2]-a[2]+a[3]-…-a[k-1]+a[k] = a[k]

然后我们用树状数组维护差分数组就好啦!

Code:

#include
#include

int tr[500010];
int n,m;

int lowbit(int x)
{
    return x&-x;
}

void add(int x,int k)
{
    while(x<=n)
    {
        tr[x]+=k;
        x+=lowbit(x);
    }
}

int sum(int x)
{
    int tot=0;
    while(x!=0)
    {
        tot+=tr[x];
        x-=lowbit(x);
    }
    return tot;
}

int main()
{
    scanf("%d %d",&n,&m);
    int x,y=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(i,x-y);
        y=x;
    }
    for(int i=1;i<=m;i++)
    {
        int p;
        scanf("%d",&p);
        if(p==1)
        {
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            add(x,z);
            add(y+1,-z);
        }
        if(p==2)
        {
            int x;
            scanf("%d",&x);
            printf("%d\n",sum(x));
        }
    }
}

你可能感兴趣的:(树状数组)