题目描述
如题,已知一个数列,你需要进行下面两种操作:
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<cstdio>
#include<cstdlib>
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));
}
}
}