首页 > 其他分享 >分块小结

分块小结

时间:2024-03-08 22:00:11浏览次数:30  
标签:5000 bel 分块 int siz 区间 区块 小结

分块概念

就是把一个长序列分成 \(\sqrt{n}\) 个区间,分别维护每个区间内的信息和,然后查询时可以优化时间复杂度。

还可以完成一些线段树完成不了的神秘操作,比如这道题

但是总体时间复杂度不如线段树,但它的扩展性比线段树还要强,因为分块中每个区间的信息和不需要具有传递性

怎么理解?

就比如说,需要对一个序列维护区间取模,我们可以开一个数组专门存储当前区间的所有数是否都小于要取模的数,以此实现修改的加速。

线段树的做法就会难想很多,不做赘述。

代码结构

预处理

预处理出每个区块的起始点和重点,以及每个数属于哪个区块。

必要时要处理处每个区块的长度(如要区间加)。

int a[100011];
int bel[100010];
int st[5000],ed[5000],siz[5000],sum[5000];
int cnt[5001],f[5001];
void init()
{
	int sq=sqrt(n);
	for(int i=1;i<=sq;i++)
	{
		st[i]=n/sq*(i-1)+1;
		ed[i]=n/sq*i;
	}
	ed[sq]=n;
	for(int i=1;i<=sq;i++)
	{
		for(int j=st[i];j<=ed[i];j++)
		{
			bel[j]=i;sum[i]+=a[j];
			if(a[j]==1) cnt[i]++;
		}
		siz[i]=ed[i]-st[i]+1;
	}
}

修改

首先判断当前要修改的区间 \([x,y]\) 是否在同一区块内:

if(bel[x]==bel[y])
{
	for(int i=x;i<=y;i++)
	{
		//process
	}
}

否则,分成三个区域修改:

  1. \([x,end[bel[x]]]\)

  2. \((bel[x],bel[y])\)

  3. \([st[bel[y]],y]\)

for(int i=x;i<=ed[bel[x]];i++)
{
	//process
}
for(int i=st[bel[y]];i<=y;i++)
{
	//process
}
for(int i=bel[x]+1;i<bel[y];i++)
{
	//process(区块整块)
}

而且,分块能加速的重要一环就是处理 \((bel[x],bel[y])\)。

查询

查询代码与修改代码大同小异,就像是树剖求树链和与树链修改的关系一样。

例题

P4145 上帝造题的七分钟 2 / 花神游历各国

link

这个题是维护区间开方和区间和,区间开方用线段树很难搞了,使用分快的思想:对于序列中最大的数 \(10^{12}\),开方 \(6\) 次就会变成 \(1\)。

因此,在修改操作中,最浪费时间的不是对于非 \(1\) 的数开方,而是对非常多的 \(1\) 进行开方。

所以,我们可以在每个区间中维护一个标记 \(flag\),表示当前区间内的所有数是否都为 \(1\)。

如果都是 \(1\),直接跳过,否则 \(O(\sqrt{n})\) 修改当前区块的值(\(sqrt\) 视为 \(O(1)\))。

对于区间和,我们可以维护区块和,每次修改区间的时候先减去当前 \(a[i]\) 的值,再给 \(a[i]\) 开方,最后把区间和加上 \(a[i]\) 的值。

这样就搞定了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int a[100011];
int bel[100010];
int st[5000],ed[5000],siz[5000],sum[5000];
int cnt[5001],f[5001];
void init()
{
	int sq=sqrt(n);
	for(int i=1;i<=sq;i++)
	{
		st[i]=n/sq*(i-1)+1;
		ed[i]=n/sq*i;
	}
	ed[sq]=n;
	for(int i=1;i<=sq;i++)
	{
		for(int j=st[i];j<=ed[i];j++)
		{
			bel[j]=i;sum[i]+=a[j];
			if(a[j]==1) cnt[i]++;
		}
		siz[i]=ed[i]-st[i]+1;
	}
}
void change(int x,int y)
{
	if(y<x) swap(x,y);//很恶心,卡了我半个小时
	if(bel[x]==bel[y])
	{
		for(int i=x;i<=y;i++)
		{
			if(a[i]==1) continue;
			sum[bel[i]]-=a[i];
			a[i]=sqrt(a[i]);
			sum[bel[i]]+=a[i];
			if(a[i]==1) cnt[bel[i]]++;
			if(cnt[bel[i]]>=siz[bel[i]]) f[bel[i]]=1;//记录区块全为 1
		}
	}
	else
	{
		for(int i=x;i<=ed[bel[x]];i++)
		{
			if(a[i]==1) continue;
			sum[bel[i]]-=a[i];
			a[i]=sqrt(a[i]);
			sum[bel[i]]+=a[i];
			if(a[i]==1) cnt[bel[i]]++;
			if(cnt[bel[i]]>=siz[bel[i]]) f[bel[i]]=1;
		}
		for(int i=st[bel[y]];i<=y;i++)
		{
			if(a[i]==1) continue;
			sum[bel[i]]-=a[i];
			a[i]=sqrt(a[i]);
			sum[bel[i]]+=a[i];
			if(a[i]==1) cnt[bel[i]]++;
			if(cnt[bel[i]]>=siz[bel[i]]) f[bel[i]]=1;
		}
		for(int i=bel[x]+1;i<bel[y];i++)
		{
			if(f[i]) continue;//精髓!!
			else
			{
				for(int j=st[i];j<=ed[i];j++)
				{
					if(a[j]==1) continue;
					sum[bel[j]]-=a[j];
					a[j]=sqrt(a[j]);
					sum[bel[j]]+=a[j];
					if(a[j]==1) cnt[bel[j]]++;
					if(cnt[bel[j]]>=siz[bel[j]]) f[bel[j]]=1;
				}
			}
		}
	}
}
int query(int x,int y)
{
	if(y<x) swap(x,y);
	int res=0;
	if(bel[x]==bel[y])
	{
		for(int i=x;i<=y;i++)	res+=a[i];
	}
	else
	{
		for(int i=x;i<=ed[bel[x]];i++)	res+=a[i];
		for(int i=st[bel[y]];i<=y;i++)	res+=a[i];
		for(int i=bel[x]+1;i<bel[y];i++)	res+=sum[i];
	}
	return res;
}
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	init();cin>>m;
	for(int i=1;i<=m;i++)
	{
		int k,x,y;
		scanf("%lld%lld%lld",&k,&x,&y);
		if(!k)	change(x,y);
		else	cout<<query(x,y)<<endl;
		
	}
}

标签:5000,bel,分块,int,siz,区间,区块,小结
From: https://www.cnblogs.com/ccjjxx/p/18061945

相关文章

  • 莫队与分块学习笔记
    分块思想介绍分块是一种思想,而不是一种数据结构。思想就是,将一块大的区间,转换成小的区间来处理。例如,在一个\(n\)长度上的数轴,我们可以将其分成\(\sqrtn\)个长度为\(\sqrtn\)的块来解决。典型问题对于一类很典型的问题,可以用分块来做。单点修改,区间查询这玩意咋......
  • Markdown基础语法学习小结
    一、前言Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式。--摘自百度百科对于程序员来说,学习并记录到博客中是非常好的学习习惯,能提升学习效果,本来想要自己搭建一个博客网站,但重心在于学习上,就不花太多时间精力......
  • tomcat白名单(八)SNI小结
    继tomcat白名单(五)其他  四层七层浏览器(客户端)dns解析connectip在clientHello中用浏览器地址栏host塞入sni在http头中塞入Host头网关(服务端)根据SNI路由根据Host头路由 hauqi,openshift根据SNI路由passthrough的tcp流量所以openshift必然会通过某种......
  • 分块 学习笔记
    目录分块思想分块基础操作2.1\(O(\sqrtn)-O(\sqrtn)\)区间加、区间查询2.2\(O(1)-O(\sqrtn)\)区间加、单点查询2.3\(O(\sqrtn)-O(1)\)单点加、区间查询各种分块思路3.1对序列分块普通区间和维护区间\(k\)大等3.2对值域分块3.3对操作分块3.......
  • Ynoi 大分块系列
    最初分块先考虑怎么用分块维护区间第\(k\)小。首先肯定想到二分区间第\(k\)小,然后查询区间有多少个数小于等于\(x\)。但这样时间复杂度是\(\operatorname{O}(n\sqrt{n}\log^2n)\)的,无法通过此题。考虑这样一个事情,我们可以暴力枚举区间第\(k\)小,然后查询区间内有多......
  • 线段树合并小结
    权值线段树就是把线段树变成桶。用线段树维护桶。代码:模板:P1138第k小整数#include<bits/stdc++.h>usingnamespacestd;intn,k;structsegmentTree{ structnode{ intsum; }tr[40000<<2]; #definelidnow<<1 #defineridnow<<1|1 voidupdate(intnow,intl......
  • 分块相关题目
    题单。UPD:题单里的题\(n=m\)。数列分块入门一看到区间修改\(+\)单点查询,考虑差分。考虑分块维护差分数组。对于修改操作,就对\(l\)位置\(+k\),\(r+1\)位置\(-k\);对于查询操作,查询\([1,x]\)的和即可。时间复杂度\(O(m\sqrt{n})\),可以通过。代码。数列分块入门二如......
  • 分块一览
    前言如题。值域分块顾名思义,就是在桶上分块。它的用处是把区间修改和区间询问中某一种操作变成\(O(1)\),另一种变成\(O(\sqrtn)\)。所以经常用来辅助维护两种操作数量严重不对等的数据结构。典型代表有莫队和根号分治。这里看一个莫队的例子。如我们要维护一个二维数点......
  • 分块和莫队
    1分块1.1概念简述分块被称为“优雅的暴力”。对于一个区间$[1,n]$,我们将其分成若干个块。在处理整块时直接维护整块信息,达到降低时间复杂度的目的。对于常规分块,设块长为$m$,则一般情况下$m$取$\sqrt{n}$时复杂度最优。下面举几例来说明分块如何降低时间复杂度。1.2......
  • 概率期望小结
    P4316绿豆蛙的归宿典型的期望dp。思路就是反向建图加反向跑dp。式子是这样的:\(\largedp[v]=\sum\frac{dp[u]+w[u\to\v]}{indeg[v]}\)然后遍历图可以使用拓扑排序或者深搜。#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;intn,m;structnod......