区间操作

由树状数组的知识引申来的
什么是树组呢 我也不说了 根据lowbit写出来的一个关系如图
在这里插入图片描述

有时候在对[l,r]一个区间操作的时候很容易t
例如对[l,r]区间都加上k ,如果for循环跑一边 复杂度有点高

现在的话我们引入差分数组
1.定义:
对于已知有n个元素的数列d,我们可以建立记录它每项与前一项差值的差分数组f:显然,f[1]=d[1]-0=d[1];对于整数i∈[2,n],我们让f[i]=d[i]-d[i-1]。

2.简单性质:
计算数列各项的值:观察d[2]=f[1]+f[2]=d[1]+d[2]-d[1]=d[2]可知,数列第i项的值是可以用差分数组的前i项的和计算的,即d[i]=f[i]的前缀和。
3.用途:
(1)快速处理区间加减操作:
假如现在对数列中区间[L,R]上的数加上x,我们通过性质(1)知道,第一个受影响的差分数组中的元素为f[L],即令f[L]+=x,那么后面数列元素在计算过程中都会加上x;最后一个受影响的差分数组中的元素为f[R],所以令f[R+1]-=x,即可保证不会影响到R以后数列元素的计算。这样我们不必对区间内每一个数进行处理,只需处理两个差分后的数即可;
(2)询问区间和问题:
由性质我们可以计算出数列各项的前缀和数组sum各项的值;那么显然,区间[L,R]的和即为ans=sum[R]-sum[L-1];

c数组时a数组的差分数组
a[1]+a[2]+…+a[n]

= (c[1]) + (c[1]+c[2]) + … + (c[1]+c[2]+…+c[n])

= n*c[1] + (n-1)*c[2] +… +c[n]

= n * (c[1]+c[2]+…+c[n]) - (0c[1]+1c[2]+…+(n-1)*c[n])
在这里插入图片描述

所以我们在add的时间 存两个数组 一个维护 c[i] 一个维护 (i-1)*c[i]

然后在进行区间修改的时间 根据之前的关系
add(c1,l,h);add(c1,r+1,-h); //c1维护c[i] 修改的时候没什么

add(c2,l,h*(l-1));add(c2,r+1,-h*r);//c2维护(i-1)*c[i] 修改的时间注意(i-1) 与之前保持一致

  • 最后进行sum求和的时间 对于第一个c1 要*所对应的倍数
    然后与第二个作差就可以
#include<bits/stdc++.h>
#include<stdio.h>
#include<string.h>
using namespace std;
#define PI 3.1415926535897932384626433832795028841971693993751058209749445923078164062
typedef long long ll;
typedef pair<int,int> PII;ll mod=1e9+7;
inline int read() {char ch = getchar(); int x = 0, f = 1;while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();} while('0' <= ch && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();} return x * f;}
int qow_m(int n,int w){int k=1;while(w){if(w&1)k=k*n%mod;	n=n*n%mod;	w>>=1;}return k%mod; }
//ll read(){ll res = 0, ch, flag = 0;if((ch = getchar()) == '-')flag = 1;else if(ch >= '0' && ch <= '9')res = ch - '0';while((ch = getchar()) >= '0' && ch <= '9' )res = res * 10 + ch - '0';return flag ? -res : res;}
const int maxn =1e6+7 ;//..ll sum=1;
ll lowbit(ll x)	{return x&(-x);}
ll n,m,k,wz,cnt=0,h,num=0;
ll x,y=0;
ll c2[maxn];
ll c1[maxn];
void add(ll *t,ll x,ll w)
{
	for(int i=x;i<=n;i+=lowbit(i))
        t[i]+=w;
}
ll sum(ll *t,ll x)
{
	ll s=0;
	for(int i=x;i>=1;i-=lowbit(i))
        s+=t[i];
	return s;
}
int main()
{
	cin>>n>>m;
	ll od=0;
	for(ll i=1;i<=n;i++)
	{
	    ll xz;
		xz=read();
		add(c1,i,xz-od);
		add(c2,i,(i-1)*(xz-od));
		od = xz;
	}
	while(m--)
	{
		k =read();
		if(k==1)
		{
		    ll l,r,h;
		    l=read();r=read();h=read();
		    if(l>r)
                swap(l,r);

			add(c1,l,h);add(c1,r+1,-h);
			add(c2,l,h*(l-1));add(c2,r+1,-h*r);
		}
		if(k==2)
		{
		    ll l,r;
		    ll sum1,sum2;
			l=read();r=read();
			if(l>r)
                swap(l,r);
			sum1=(l-1)*sum(c1,l-1)-sum(c2,l-1);
			sum2=r*sum(c1,r)-sum(c2,r);
			printf("%lld\n",sum2-sum1);
		}
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛郎恋刘娘,刘娘念牛郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值