专栏文章

题解:P13976 数列分块入门 1

P13976题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mio073va
此快照首次捕获于
2025/12/02 11:13
3 个月前
此快照最后确认于
2025/12/02 11:13
3 个月前
查看原文
虽然这题是数列分块题,但是这题也可以用线段树的方案解决,可以发现是单点查询区间更新问题,所以需要懒标记,设 toAdd 表示懒标记,则我们每次在线段树上更新时候,就采用区间三分离的方法,当无贡献的时候就直接返回,当整体贡献的时候这个整体的懒标记都加上 cc,否则就在子树中递归,直到整体贡献为止,就实现了一个基本的区间加。
然后如何单点查询,我们单点二选一。如果已经拆分成最小的区间(点),直接返回这个点的懒标记即可。否则二选一:若在左子树内,即当前询问的 idid 在左子树的右端点以内,则左子树产生贡献,继续递归并在递归结束以后加上这个点的懒标记,否则右子树产生贡献,同样递归右子树加上这个点的懒标记。其实每次询问一个点,就是某个叶子到根的点权和,而这个叶子就是代表区间 [id,id][id,id] 的。
这样一步步模拟实现即可,时间复杂度 O(nlogn)O(n \log n) 的。
AC 代码:
CPP
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=500009;
const ll MOD=100000007;
ll n,x[N];
struct Segment{
	ll l,r;
	ll toAdd;
};
Segment tr[N*4];
void build(ll u,ll l,ll r){
	if(l==r){
		tr[u]=(Segment){l,r,x[l]};
		return;
	}
	ll mid=(l+r)/2;
	build(u*2,l,mid);
	build(u*2+1,mid+1,r);
	tr[u]=(Segment){l,r,0};
}
void add(ll u,ll l,ll r,ll delta){
	if(r<tr[u].l||tr[u].r<l) return;
	if(l<=tr[u].l&&tr[u].r<=r){
		tr[u].toAdd+=delta;
		return;
	}
	add(u*2,l,r,delta);
	add(u*2+1,l,r,delta);
}
ll value(ll u,ll id){
	if(tr[u].l==tr[u].r)
		return tr[u].toAdd;
	if(id<=tr[u*2].r)
		return tr[u].toAdd+value(u*2,id);
	else
		return tr[u].toAdd+value(u*2+1,id);
}
int main(){
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++) scanf("%lld",&x[i]); 
	build(1,1,n);
	for(ll i=1;i<=n;i++){
		ll op,l,r,c;
		scanf("%lld %lld %lld %lld",&op,&l,&r,&c);
		if(op==0) add(1,l,r,c);
		else printf("%lld\n",value(1,r));
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...