树状数组区间修改区间查询

众所周知树状数组是很强大的
目前已知可以维护前缀和,不修改的前缀最值,前缀位运算等等
反正总之就是一个巨强的前缀数据结构
如果需要区间修改单点查询,可以差分解决
那么区间修改区间查询呢。
举个例子:

  • a[1]~a[n]的数组,每次操作要么给a[l]~a[r]每个位置+x,要么查询a[l]~a[r]的和,操作数105

同样考虑差分,记 c [ i ] = a [ i ] − a [ i − 1 ] c[i]=a[i]-a[i-1] c[i]=a[i]a[i1],那么
∑ i = 1 n c [ i ] = a [ n ] \sum_{i=1}^nc[i]=a[n] i=1nc[i]=a[n]
区间修改a[l]~a[r]相当于给c[l]+x,c[r+1]-x

要区间查询,那么记 b [ i ] = ∑ j = 1 i a [ j ] b[i]=\sum_{j=1}^{i}a[j] b[i]=j=1ia[j],要求的相当于就是b的单点查询
b [ n ] = ∑ i = 1 n a [ i ] = ∑ i = 1 n ∑ j = 1 i c [ i ] = ∑ i = 1 n c [ i ] ∗ ( n − i + 1 ) = ( ∑ i = 1 n c [ i ] ) ∗ ( n + 1 ) − ( ∑ i = 1 n c [ i ] ∗ i ) b[n]=\sum_{i=1}^na[i]=\sum_{i=1}^n\sum_{j=1}^ic[i]\\ =\sum_{i=1}^nc[i]*(n-i+1)\\ =\left(\sum_{i=1}^nc[i]\right)*(n+1)-\left(\sum_{i=1}^nc[i]*i\right) b[n]=i=1na[i]=i=1nj=1ic[i]=i=1nc[i](ni+1)=(i=1nc[i])(n+1)(i=1nc[i]i)
只需要两个树状数组,分别维护c[i]的前缀和,c[i]*i的前缀和即可

LL arr[2][maxn];
inline void upd(int x,int d)//修改c[x]
{
	for(int i=x;i<=n;i+=i&-i)
		arr[0][i]+=d,arr[1][i]+=1ll*d*x;//分别维护c[x],c[x]*x的前缀和
}
inline LL qsum(int x)
{
	LL s[2]={0};
	for(int i=x;i;i-=i&-i) s[0]+=arr[0][i],s[1]+=arr[1][i];
	return s[0]*(x+1)-s[1];
}
inline void modify(int l,int r,int d)
{
	upd(l,d),upd(r+1,-d);
}
inline LL query(int l,int r)
{
	return qsum(r)-qsum(l-1);
}

区间修改实际上就是差分再差分,最后变成一个单点问题,就可以用树状数组了,拓展到矩形求和,或者更高维也是可以的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值