题意:给出N个数,进行Q次操作,有两种操作,一种是区间查询,一种是区间更新.
由于这题感觉没什么附加条件,比较裸,博主懒得写线段树,直接上了树状数组,但是区间更新的时候我是区间内每个点去更新的,所以返回结果果断tle.然后去网上学了一发树状数组区间更新的姿势,比线段树要快,也很巧妙.
用两个bit数组维护前缀和增量 即二维数组bit[2][maxn]
当[l,r]区间增加c时, 不难得出, [1,l-1] 前缀和不变, [l,r]区间内的前缀和增加量为c* (i-l+1), [r+1,N ]区间增量为c*(r-l+1);
建立增加量和i之间的坐标轴,可以看出在l到r区间内点的分布是一条直线, 所以我们可以把斜率用bit[0]数组来维护斜率, bit[1]数组来维护常量.
维护操作
第一步: 当[l,r]区间内增加c时,即斜率增加c,这时候bit[0]在l这个位置加上c,由于在r+1到n这个区间多加上了c,所以要在r+1这个位置上减去c.
第二步: 第一步对斜率进行了维护,第二步则需要改变常量,所以bit[1]在l这个位置上应当加上c*(-l+1),这样表示在l到N内更新了bit[1]的值
第三步: 最后更新r之后的增量,因为c*(-l+1)在第二部已经更新过了,所以只要bit[1]在r+1位置上加上c*r就行;
AC时间:1954ms 线段树时间在2300到3000.
所以树状数组做一些裸的区间题在代码量和效率上还是比较占优的.
代码:
#include
#include
#include
#include
using namespace std;
int n, q;
long long a[101234];
long long bit[2][101234];
long long pesum[101234];
int lowbit(int x)
{
return x&-x;
}
void add(long long b[], int x, long long y)
{
while(x<=n)
{
b[x]+=y;
x+=lowbit(x);
}
}
long long sea(long long b[], int x)
{
long long ret=0;
while(x>0)
{
ret+=b[x];
x-=lowbit(x);
}
return ret;
}
long long queery(int x)
{
return sea(bit[0], x)*x+sea(bit[1], x);
}
int main()
{
memset(bit, 0, sizeof(bit));
memset(pesum, 0, sizeof(pesum));
scanf("%d%d", &n, &q);
int i, j;
for(i=1; i<=n; i++)
{
scanf("%lld", &a[i]);
pesum[i]+=a[i]+pesum[i-1]; //求前缀和
}
for(i=1; i<=q; i++)
{
char c,d;
scanf("%c%c", &d, &c);
if(c=='C')
{
int a, b; long long y;
scanf("%d%d%lld", &a, &b, &y);
add(bit[0], a, y); //对应第一步
add(bit[0], b+1, -y); //对应第一步
add(bit[1], a, (1-a)*y); // 对应第二部
add(bit[1], b+1, y*b); //对应第三步
}
if(c=='Q')
{
int a, b;
scanf("%d%d", &a, &b);
printf("%lld\n", queery(b)-queery(a-1)-pesum[a-1]+pesum[b]); //区间值加上区间增量
}
}
return 0;
}