zl程序教程

您现在的位置是:首页 >  其他

当前栏目

LibreOJ#132. 树状数组 3 :区间修改,区间查询

2023-04-18 15:51:27 时间

【区间修改,区间查询】
https://loj.ac/p/132

题意

解析

代码

做的时候是按照洛谷线段树1那题做的,范围10^18。但其实会有问题,求和过程中可能会爆longlong。
求一次前缀,实际上就是
这是sum(k)

区间查询 就是求 sum(r) - sum(l)
用两个树状数组,一个实现(D_i) 另一个实现(i-1)*(D_i)
方便无脑,直接搞两个add和sum。其实也可以用传数组的形式。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;

ll tr1[N],tr2[N],n,m;

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

void add1(ll x,ll k){
	while(x <= n){
		tr1[x] += k;
		x = x + lowbit(x);
	}
}

void add2(ll x,ll k){
	while(x <= n){
		tr2[x] += k;
		x = x + lowbit(x);
	}
}

ll sum1(ll x){
	ll ans = 0;
	while(x >= 1){
		ans += tr1[x];
		x = x - lowbit(x);
	}
	return ans;
}

ll sum2(ll x){
	ll ans = 0;
	while(x >= 1){
		ans += tr2[x];
		x = x - lowbit(x);
	}
	return ans;
}

int main(){
	scanf("%lld %lld",&n,&m);
	int last = 0;
	for(int i=1;i<=n;i++){
		ll x;
		scanf("%lld",&x);
		add1(i,x-last);
		add2(i,(x-last)*(i-1));
		last = x;
	}

	while(m--){
		ll op,x,y,k;
		scanf("%lld %lld %lld",&op,&x,&y);
		if(op == 1){
			scanf("%lld",&k);
			add1(x,k);
			add1(y+1,-k);
			add2(x,k*(x-1));//维护(i-1)*D[i]
			add2(y+1,-k*y);//k*y = k*((y+1)-1)
		}else{
			//注意 如果范围比较大 y * sum1(y) 会爆longlong
			printf("%lld
", y*sum1(y)-(x-1)*sum1(x-1)-(sum2(y)-sum2(x-1)));
		}
	}

	return 0;
}