P3372 【模板】线段树 1(树状数组解法)

P3372 【模板】线段树 1

题目描述
如题,已知一个数列,你需要进行下面两种操作:
将某区间每一个数加上 kk。
求出某区间每一个数的和。
输入格式
第一行包含两个整数 n, mn,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。
接下来 mm 行每行包含 33 或 44 个整数,表示一个操作,具体如下:
1 x y k:将区间 [x, y][x,y] 内每个数加上 kk。
2 x y:输出区间 [x, y][x,y] 内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 的结果。

首先我们得晓得基本的树状数组的可以完成的操作是:
1单点修改
2区间查询

但是如果预处理了成了差分树状数组的话就可以完成
1区间修改
2单点查询

而现在题目的意思是区间修改区间查询
现在定义数组a是原数组
b是a的差分数组
现在我们想要求a[1]+a[2]+a[3]+…+a[x];
也就是求a的某个前缀和
a[1]+a[2]+a[3]+…+a[x]=b[1]+(b[1]+b[2])+(b[1]+b[2]+b[3])+…(b[1]+b[2]+…b[x])
将这个公式竖着放一下
a[1]=b[1]
a[2]=b[1]+b[2]
a[3]=b[1]+b[2]+b[3]
a[4]=b[1]+b[2]+b[3]+b[4]
.
.
.
a[x]=b[1]+b[2]+b[3]+b[4] …+b[x]

然后这样补全
第0行b[1]+b[2]+b[3]+b[4] …+b[x]
第1行b[1]+b[2]+b[3]+b[4] …+b[x]
第2行b[1]+b[2]+b[3]+b[4] …+b[x]
第3行b[1]+b[2]+b[3]+b[4] …+b[x]
第4行b[1]+b[2]+b[3]+b[4] …+b[x]
.
.
.
第x行b[1]+b[2]+b[3]+b[4] …+b[x]
我们将上面看成一个矩阵
现在我们需要求的就是加粗部分
加粗部分=整个矩阵-非加粗部分
整个矩阵=(x+1)*(a[1]+a[2]+…a[x])
//上面x+1的因为多补了一行
非加粗部分=1 * b[1]+2 * b[2]+3 * b[3]+…x * b[x]
//这里就是多补一行的原因 让后这一部分变成i * b[i]的形式
加粗部分=(x+1)* (b[1]+b[2]+…b[x])-(1* b[1]+2* b[2]+3* b[3]+…x* b[x])=(x+1)(b的前缀和)-(ib[i]的前缀和)
那么我们只需要搞2个树状数组tr1(维护b)tr2(维护i*b[i])
每次logn时间查询差分数组前缀和 还有i*b[i]的前缀和
区间修改也就是改差分数组2个点就ok了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
const int N=1e6+100;
LL tr1[N];//维护第差分数组b 
LL tr2[N];//维护i *b[i]数组 
int lowbit(int i)//取二进制最低位1 
{
	return i&-i;
}
void add(LL tr[],int i,LL x)//单点修改 
{
	for(;i<=n;i+=lowbit(i))
	{
		tr[i]+=x;
	}
	return ;
}
LL ask(LL tr[],int i)//区间查询 
{
	LL sum=0;
	for(;i;i-=lowbit(i))
	{
		sum+=tr[i];
	}
	return sum;
}
LL fun(int i)//按照推的公式求值 
{
	return (i+1)*ask(tr1,i)-ask(tr2,i);
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int m;
	cin>>n>>m;
	LL x1,x2;
	x1=0;
	for(int i=1;i<=n;i++)
	{
		cin>>x2;
		add(tr1,i,x2-x1);
		add(tr2,i,i*(x2-x1));
		x1=x2;
	}
	while(m--)
	{
		int f;
		cin>>f;
		if(f==1)
		{
			int l,r;
			LL d;
			cin>>l>>r>>d;
			add(tr1,l,d),add(tr1,r+1,-d);
			add(tr2,l,l*d),add(tr2,r+1,(r+1)*(-d));
		}
		else 
		{
			int r,l;
			cin>>l>>r; 
			LL t=fun(r)-fun(l-1);
			cout<<t<<endl;
		}
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值