洛谷-P3372-(分块)

P3372

题意:
就是有个初始序列,然后m次操作,一种是让l到r都加上k,一种是查询l到r的区间和。

思考:
其实就是线段树板子题,这里用来练习分块。

代码:

int T,n,m,k;
int va[N];
int pos[N],l[N],r[N],sum[N],add[N],cnt;
/*va是某个点的值,sum是某个块的值,add是某个块的标记
为什么还要add呢,因为如果更新块的时候,直接更新了sum。如果下次查询l,r你说怎么累加这个和,如果输出sum[pos[l]]这太大了,如果输出l到r的va值,这又是错的因为你只更新了sum没有更新va,所以为了这样的情况,要维护3个,一个是单点值,一个是某个块的值,一个是标记。
*/
void build()
{
	int siz = sqrt(n);
	cnt = (n+siz-1)/siz;
	for(int i=1;i<=cnt;i++)
	{
		l[i] = (i-1)*siz+1;
		r[i] = i*siz;
	}
	r[cnt] = n;
	for(int i=1;i<=n;i++) pos[i] = (i-1)/siz+1;
	for(int i=1;i<=n;i++) sum[pos[i]] += va[i];
}

void update(int a,int b,int value)
{
	if(pos[a]==pos[b])
	{
		for(int i=a;i<=b;i++) va[i] += value;
		sum[pos[a]] += (b-a+1)*value;
		return ;
	}
	for(int i=a;i<=r[pos[a]];i++) va[i] += value;
	sum[pos[a]] += (r[pos[a]]-a+1)*value;
	for(int i=l[pos[b]];i<=b;i++) va[i] += value;
	sum[pos[b]] += (b-l[pos[b]]+1)*value;
	for(int i=pos[a]+1;i<pos[b];i++) add[i] += value;
}

int query(int a,int b)
{
	int ans = 0;
	if(pos[a]==pos[b])
	{
		for(int i=a;i<=b;i++) ans += va[i];
		ans += add[pos[a]]*(b-a+1);
		return ans;
	}
	for(int i=a;i<=r[pos[a]];i++) ans += va[i];
	ans += add[pos[a]]*(r[pos[a]]-a+1);
	for(int i=l[pos[b]];i<=b;i++) ans += va[i];
	ans += add[pos[b]]*(b-l[pos[b]]+1);
	for(int i=pos[a]+1;i<pos[b];i++) ans += sum[i]+add[i]*(r[i]-l[i]+1);
	return ans;
}

signed main()
{
	IOS;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>va[i];
	build();
	while(m--)
	{
		int op,a,b,c;
		cin>>op>>a>>b;
		if(op==1)
		{
			cin>>c;
			update(a,b,c);
		}
		else cout<<query(a,b)<<"\n";
	}
	return 0;
}

总结:
多多积累经验,多思考思考。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值