【ybt高效进阶4-2-4】区间修改区间查询 【树状数组模板】

区间修改区间查询 【ybt高效进阶4-2-4】

题目大意:

给定一个数组,让其支持区间修改,区间求和的操作。

思路:

树状数组+差分
我们设 d d d数组是 a a a数组的差分数组,那么我们有 a [ i ] = ∑ j = 1 i d [ j ] a[i]=\sum\limits_{j=1}^{i}d[j] a[i]=j=1id[j]
那么求区间的前缀和类比单点修改区间查询,用相减法,区间末值-区间开头。
那么我们有: ∑ i = 1 n a [ i ] = ∑ i = 1 n ∑ j = 1 i d [ j ] \sum\limits_{i=1}^{n}a[i]=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i}d[j] i=1na[i]=i=1nj=1id[j]
展开这个式子,观察每个项,我们发现, d [ 1 ] d[1] d[1]出现了 n n n次, d [ 2 ] d[2] d[2]出现了 n − 1 n-1 n1次,以此类推, d [ i ] d[i] d[i]出现了 n − i + 1 n-i+1 ni+1次。于是我们得到了: ∑ i = 1 n a [ i ] = ∑ i = 1 n d [ i ] ∗ ( n − i + 1 ) = ∑ i = 1 n d [ i ] ∗ ( n + 1 ) − d [ i ] ∗ i \sum\limits_{i=1}^{n}a[i]=\sum\limits_{i=1}^{n}d[i]*(n-i+1)=\sum\limits_{i=1}^{n}d[i]*(n+1)-d[i]*i i=1na[i]=i=1nd[i](ni+1)=i=1nd[i](n+1)d[i]i
那么我们使用树状数组维护 d [ i ] d[i] d[i] d [ i ] ∗ i d[i]*i d[i]i这两个值就好了,差分时记得右边界要+1!!!

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<vector>
#define r register
#define rep(i,x,y) for(r ll i=x;i<=y;++i)
#define per(i,x,y) for(r ll i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const ll V=1e6+50,p=1e9+7;
ll n,m,c[2][V],a[V];
ll x,y,k,otp;
ll lowbit(ll x) { return x&(-x); }
void add(ll x,ll y)
{
	for(r ll i=x;i<=n;i+=lowbit(i))
	{
		c[0][i]+=y;
		c[1][i]+=y*x; //两个树状数组维护上面所说的值
	}
}
ll ask(ll x)
{
	ll res=0;
	for(r ll i=x;i;i-=lowbit(i))
	 res+=c[0][i]*(x+1)-c[1][i]; //代入公式
	return res;
}
char buf[1<<23], *p1=buf, *p2=buf, obuf[1<<23], *O=obuf; //超级加速快读
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<21, stdin), p1 == p2) ? EOF : *p1++)
ll in()
{
	ll res=0,f=1;
	char ch;
	while((ch=getchar())<'0'||ch>'9')
	 if(ch=='-') f=-1;
	res=res*10+(ch-'0');
	while((ch=getchar())>='0'&&ch<='9')
	 res=res*10+(ch-'0');
	return res*f;
}
void put(ll x)//普通的快输
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) put(x/10);
	putchar(x%10+'0');
}
int main()
{
	n=in(),m=in(); //建议使用快读,要不很可能会被卡常
	rep(i,1,n)
	{
		a[i]=in();
		add(i,a[i]);
		add(i+1,-a[i]);
	}
	rep(i,1,m)
	{
		otp=in(); 
		if(otp==1)
		{
			x=in(),y=in(),k=in();
			add(x,k);
			add(y+1,-k);//右边界要+1
		}
		else 
		{
			x=in(),y=in();
			put(ask(y)-ask(x-1));
			putchar(10);
		}
	}
 	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值