Codevs2038 线段树练习3(分块)

博客介绍了Codevs网站中的一道题目,通过使用分块策略来解决,将原序列分为sqrt(N)块。对于修改操作,暴力重构两端块并为中间块打tag,查询时重构两端并直接统计中间块。文章提到了三种解题方法:线段树、树状数组和分块,并提供了相关链接详细解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接

  http://codevs.cn/problem/1082/

题解

  这道题目可以用分块做。

  把原序列分成sqrt(N)块,对于每次修改,直接把两头的块暴力重构,中间的可以打上一个tag。查询时,暴力重构两头的(此时下放tag),中间的直接统计就好。

  至此,这道题的三种做法就全了:

  1.线段树

  2.树状数组(升级版):http://blog.youkuaiyun.com/fsahfgsadhsakndas/article/details/52650026

  3.分块:http://blog.youkuaiyun.com/fsahfgsadhsakndas/article/details/54772801

代码

//分块
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 300000
#define ll long long
using namespace std;
ll N, lp[maxn], tag[maxn], size, num[maxn], sum[maxn];
void input()
{
	ll i;
	scanf("%lld",&N);
	size=sqrt(N);
	for(i=0;i<N;i++)
	{
		scanf("%lld",num+i);
		sum[lp[i]=i/size]+=num[i];
	}
	for(i=N;i<=250000;i++)lp[i]=i/size;
}
void rebuild(ll x)
{
	ll i;
	sum[x]=0;
	for(i=x*size;lp[i]==x;i++)num[i]+=tag[x],sum[x]+=num[i];
	tag[x]=0;
}
void add(ll l, ll r, ll v)
{
	ll i;
	for(i=l;lp[i]==lp[l] and i<=r;i++)num[i]+=v;
	if(lp[l]!=lp[r])for(i=r;lp[i]==lp[r];i--)num[i]+=v;
	rebuild(lp[l]), rebuild(lp[r]);
	for(i=lp[l]*size+size;lp[i]<lp[r];i+=size)tag[lp[i]]+=v;
}
ll getsum(ll l, ll r)
{
	ll i, ans=0;
	rebuild(lp[l]), rebuild(lp[r]);
	for(i=l;lp[i]==lp[l] and i<=r;i++)ans+=num[i];
	if(lp[l]!=lp[r])for(i=r;lp[i]==lp[r];i--)ans+=num[i];
	for(i=lp[l]*size+size;lp[i]<lp[r];i+=size)ans+=sum[lp[i]]+size*tag[lp[i]];
	return ans;
}
int main()
{
	ll a, b, v, M, type;
	input();
	scanf("%lld",&M);
	while(M--)
	{
		scanf("%lld%lld%lld",&type,&a,&b);
		if(type==1)scanf("%lld",&v),add(a-1,b-1,v);
		else printf("%lld\n",getsum(a-1,b-1));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值