分块算法.

分块算法用于解决区间的查询问题。

分块算法,故名思意,就是把一个数组分为长度为 n \sqrt{n} n 的数个小块,对每个小块进行单独的维护。

需要用到的数组有:

ll st[1000100],ed[1000100],a[1000100];
ll sum[1000100],add[1000100],pos[1000100];

以上数组从上往下从左到右依次为:每一个区块在a中的的首下标和末下标,原数组a;更改之后的第i个块的总值,对整个块进行更新的值,a中每一个元素所在的块的标号。(具体作用会在下面解释)

在开始查询操作之前,需要先对部分数组进行预处理:将块划分好,并将每个块的sum值更新。

	for(ll i = 1; i <= n; i++)
	{
		cin>>a[i];
		pos[i] = (i-1)/len+1;
		sum[(i-1)/len+1]+=a[i];
	}
	ll maxx = n%len?n/len+1:n/len;//maxx代表分出的总块数
	for(ll i = 1; i <= maxx; i++)
	st[i] = (i-1)*len+1,ed[i] = i==maxx?n:i*len;

在进行区间更改时,需要判断给出的区间 [ l , r ] [l,r] [l,r]是否在一个块内,如果是,则需要对这个块的所有元素进行单独更改,再将更改后的值更新到sum数组里。
否则,就对跨过的所有整块进行更新,此时用add数组来表示每个整块中每个数更新的值,同时对于那些处于两端的不包含整个小块的区间中的数做单独处理,并将处理后的值更新到sum数组中。

void change(ll l,ll r,ll w)
{
	ll p = pos[l],q = pos[r];
	if(p == q)
	{
		for(ll i = l; i <= r; i++)
		{
			a[i] += w;
		}
		for(int i = l; i <= r; i++)sum[i]+=a[i];
	}
	else
	{
		for(ll i = p+1; i <= q-1; i++)add[i]+=w;
        for(ll i = l; i <= ed[p]; i++)a[i]+=w;
        for(ll i = st[p]; i <= ed[p]; i++)sum[p] += a[i];
    	for(ll i = st[q]; i <= r; i++)a[i]+=w;
    	for(ll i = st[q]; i <= ed[q]; i++)sum[q] += a[i];
	}
}

在进行区间查询时,同样需要对其进行区间更改时的判断。
对于第i块,它的总值为sum[i]+add[i],而对于那些非整块,它的总值为各元素相加同时再加上每个元素对应的小块所对应的add*元素的数量。

ll ans = 0;
	ll p = pos[l],q = pos[r];
	if(p == q)
	{
		for(int i = l; i <= r; i++)ans+=a[i];
		ans+=(r-l+1)*add[p];
		
	}
	else
	{
		ll num = 0;
		for(ll i = p+1; i <= q-1; i++)
		{
			ans += sum[i]+add[i]*len;//len为每个小块的长度
		}
		for(int i = l; i <= st[p]; i++)ans+=a[i];
		ans+=(st[p]-l+1)*add[p];
		for(int i = st[q]; i <= r; i++)ans+=a[i];
		ans+=(r-st[q]+1)*add[q];
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值