线段树

线段树,是一种可以在 O ( l o g n ) O(logn) O(logn)复杂度内维护区间操作的数据结构,它可以用于维护区间和,区间更新以及单点更新等等,使用起来也是非常灵活的。关于线段树的信息可以自己百度一下。我们先来看题。
传送门:
https://nanti.jisuanke.com/t/A2007
这道题要我们维护的是区间和,但这个区间和是带权的。它同时也要支持单点的更新。为了实现这一操作,我们首先想到的线段树。怎么维护呢?可以这样来想,首先,利用线段树维护区间的和 s u m 1 sum_1 sum1,在维护一个加权和 s u m 2 sum_2 sum2,这个加权和是这样的,对于 a i a_i ai,我们维护 a i ⋅ ( n − i + 1 ) a_i \cdot(n-i+1) ai(ni+1)的区间和。那么对于查询区间 [ l , r ] [l,r] [l,r],显然对于前缀是可以不用动的,只需要扣除后缀的和,答案为 s u m 2 − s u m 1 ⋅ ( n − r ) sum_2-sum_1 \cdot(n-r) sum2sum1(nr)

AC代码实现

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
struct node{
	int left,right;
	ll sum1,sum2;		//sum1初始和,sum2加权和;
}tree[4*maxn];
ll ans1,ans2;

void build(int l,int r,int k,int n){
	tree[k].left=l;
	tree[k].right=r;
	if(l==r){
		scanf("%d",&tree[k].sum1);	
		tree[k].sum2=tree[k].sum1*(n-l+1);
		return;
	}

	int mid=(tree[k].left+tree[k].right)/2;
	build(l,mid,2*k,n);
	build(mid+1,r,2*k+1,n);
	tree[k].sum1=tree[2*k].sum1+tree[2*k+1].sum1;
	tree[k].sum2=tree[2*k].sum2+tree[2*k+1].sum2;
}

void update(int k,int x,int y,int n){		//更新修改值;
	if(tree[k].left==tree[k].right){
		tree[k].sum1=y;		//把sum1的值修改为sum1;
		tree[k].sum2=tree[k].sum1*(n-x+1);
		return;
	}

	int mid=(tree[k].left+tree[k].right)/2;
	if(x<=mid) update(2*k,x,y,n);
	if(x>mid)  update(2*k+1,x,y,n);
	tree[k].sum1=tree[2*k].sum1+tree[2*k+1].sum1;
	tree[k].sum2=tree[2*k].sum2+tree[2*k+1].sum2;
}
		
void query(int k,int x,int y){		//查询区间和;
	if(tree[k].left>=x&&tree[k].right<=y){
		ans1+=tree[k].sum1;
		ans2+=tree[k].sum2;
		return;
	}

	int mid=(tree[k].left+tree[k].right)/2;
	if(x<=mid)	query(2*k,x,y);
	if(y>mid)	query(2*k+1,x,y);
}

int main(){
	int n,q;
	scanf("%d %d",&n,&q);
	build(1,n,1,n);		//创建线段树;

	int op,l,r;
	while(q--){
		scanf("%d %d %d",&op,&l,&r);
		if(op==1){
			ans1=ans2=0;
			query(1,l,r);
			ll ans=ans2-ans1*(n-r);
			printf("%lld\n",ans);
		}else if(op==2){
			update(1,l,r,n);
		}
	}
	return 0;
}

新的开始,每天都要快乐哈!
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值