【分块模板】P2068统计和

  题目传送门:统计和 - 洛谷

题目大意

给定一个长度为 n,初始值都为 0 的序列 ,有w次操作,每次操作:

x a b 表示将a的值加上b,y a b 表示查询a到b的数字和。

思路

这题其实有很多做法,包括线段树、树状数组等。

但大家既然是来看板子的,那就用分块的写法。先画个图理解一下:

e0e3e6ef9cdf4747919a9bb1e6ab128f.png

这里,我们把n分成了  eq?%5Csqrt%20n 块,对于其中的每一块,我们都可以直接用for循环求出其区间和。

那查询怎么做呢?分三种情况:

44fb0de600ab42d79d21f1213b2ff8c1.png53b3396b4ff1454caf1c1410490c8365.png
430f6ce82a6a4240afc97e1862469150.png

三种情况中,红色部分可以直接用for循环求,而蓝色部分则是通过访问单个块内的区间和得到的

代码

#include<iostream>
#include<cmath>
using namespace std;
#define MaxN 100005
#define MaxB 320
#define For(i, j, k) for(int i = j; i <= k; i++)
#define int long long

int n, bs, mb;
//bs:每个块的大小;mb:块的数量
int block[MaxN], a[MaxN];
//block[i]:第i个点所属的块
int l[MaxB], r[MaxB];
//l[i]/r[i]:第i个块左端点和右端点
long long Sum[MaxB];

void init(){
	bs = sqrt(n);
	For(i, 1, n){
		block[i] = (i-1) / bs + 1;
		Sum[block[i]] += a[i];
	}
	mb = (n-1) / bs + 1;
	For(j, 1, mb){            //计算每个块的左右端点
		l[j] = (j-1) * bs + 1;
		r[j] = j * bs;
	}
	r[mb] = n;
}

int query(int x, int y){        //查询[x, y]区间的总和
	int ans = 0;
	if(block[x] == block[y]){    
		For(i, x, y) ans += a[i];    //两边直接相加
	} else{
		For(i, block[x]+1, block[y]-1) ans += Sum[i];
		For(i, x, r[block[x]]) ans += a[i];
		For(i, l[block[y]], y) ans += a[i];
	}
	return ans;
}

void modify(int x, int y){        //将x改为y
	a[x] += y; Sum[block[x]] = 0;    
	For(i, l[block[x]], r[block[x]])
		Sum[block[i]] += a[i];
}

signed main()
{
	int q, x, y;
    char op;
	cin >> n;
	For(i, 1, n) a[i] = 0;
	init();
	cin >> q;
	while(q--){
		cin >> op >> x >> y;
		if(op == 'y') cout << query(x, y) << endl;
		else modify(x, y);
	}
	return 0;
}

总结

各个数据结构的模板题,

即区间查询、单点修改的区间求和问题,可以说是入门数据结构的基础了,十分重要。

最后,如果你觉得这篇文章还不错,可以点个关注点个赞,这是免费的,你可以随时取消。你的支持永远是作者最大的动力!感谢大家的观看,我们下次见!

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

起床气233

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

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

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

打赏作者

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

抵扣说明:

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

余额充值