分块(序列分块)

分块

定义

是一种分治思想,通常指的是序列分块。对于区间修改和区间询问总是分成左端不完整的块、中间完整的块、右端不完整的块三个部分处理

与线段树的区别

使用范围更广泛,但时间复杂度更高。

修改操作

维护一个区间标记 tag_itagi​,表示第 ii 个块的修改值。

分情况讨论:

  1. 区间 [L,R] 在同一块中,暴力修改 aa 数组以及原块的信息 sumsum 数组。
  2. 区间 [L,R] 不在同一块中,分成三部分: ①. 左端不完整的块和右端不完整的块,参照情况 1 暴力。 ②. 中间完整的块,tag_i+=valtagi​+=val

带修改询问

情况1:询问区间 [L,R] 在一个块中,暴力枚举 a_iai​ 和 tagtag 数组。

情况2:区间 [L,R] 不在同一块中,分为三个部分:

  1. 左端和右端不完整的块参照情况 1。
  2. 中间完整的块要处理 sum_isumi​ 和 tag_itagi​。

模板代码

  1. 线段树1分块版
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int a[N];
int L[N], R[N], pos[N], n, m, t, tag[N], sum[N];
void update(int lt, int rt, int val)
{
	int x=pos[lt], y=pos[rt];
	if(x==y)
	{
		for(int i=lt;i<=rt;i++)
		{
			a[i]+=val;
			sum[x]+=val;
		}
	}
	else
	{
		for(int i=x+1;i<=y-1;i++)
		{
			tag[i]+=val;
		}
		for(int i=lt;i<=R[x];i++)
		{
			a[i]+=val, sum[x]+=val;
		}
		for(int i=L[y];i<=rt;i++)
		{
			a[i]+=val;
			sum[y]+=val;
		}
	}
	return ;
}
int query(int lt, int rt)
{
	int x=pos[lt], y=pos[rt];
	int ans=0;
	if(x==y)
	{
		for(int i=lt;i<=rt;i++)
		{
			ans+=a[i];
		}
	}
	else
	{
		for(int i=x+1;i<=y-1;i++)
		{
			ans+=sum[i]+tag[i]*(R[i]-L[i]+1);
		} 
		for(int i=lt;i<=R[x];i++)
		{
			ans+=a[i]+tag[x];
		}
		for(int i=L[y];i<=rt;i++)
		{
			ans+=a[i]+tag[y];
		}
	}
	return ans;
}
signed main()
{
	cin>>m;
	cin>>n;
    for(int i=1;i<=m;i++)
    {
        cin>>a[i];
    }
    t=sqrt(m);
    for(int i=1;i<=t;i++)
    {
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    if(R[t]<m)
    {
        t++;
        L[t]=R[t-1]+1;
        R[t]=m;
    }
    for(int i=1;i<=t;i++)
    {
        for(int j=L[i];j<=R[i];j++)
        {
            pos[j]=i;
            sum[i]+=a[j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        int opt;
        cin>>opt;
        if(opt==1)
        {
        	int x, y, k;
        	cin>>x>>y>>k;
        	update(x,y,k);
		}
		else
		{
			int x, y;
			cin>>x>>y;
			cout<<query(x,y)<<"\n";
		}
    }
}

线段树2就不放了。

例题

First:LOJ6278

询问:单独一块时暴力。否则不完整的块暴力,完整的块二分。

每次需要高频率还原 numnum 动态数组。

还原函数:

void resort(int x)
{
	num[x].clear();
	for(int i=L[x];i<=R[x];i++)
	{
		num[x].push_back(a[i]);
	}
	sort(num[x].begin(), num[x].end());
}
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值