分块进阶

分块进阶

分块的其他用法

P3793 由乃救爷爷

题意

题意是说对于一个长度为 \(n\)\(a\) 序列有 \(m\) 次询问,每一次需要回答一个区间的最大值大小
时限 : 5s
内存 : 500MB

思路

\(st\)

由于没有修改,全都是查询,可以想到 \(st\)

\(st\) 表需要 \(O(n\;log\,n)\) 的时间预处理,在此查询只需要 \(O(1)\) 单从时间上来看是可以通过这道题的,但是空间也许要 \(O(n\;log\,n)\) 的时间复杂度,显然超过了限制

分块

分块中的空间复杂度是在限制之内的
可是分块的每一次查询都有 \(O(3\sqrt{n})\) 的时间复杂度 \(2e7\) 的查询次数显然超时

分块 \(+\) \(st\)

既然 分块 和 \(st\)表 都不能通过这题,那么可以考虑把两个结合起来
回忆一下分块求最大值的过程

1.整块
第一个部分是中间的完整块,如果对每一完整快一个一个取最大值,显然超时,所以需要瞬间求出这一部分的最大值

可以想到用 \(st\) 表加速这个过程!
将每一个块当做一个元素,放在 \(st\)表 里预处理,\(st[\,i\,][\,j\,]\) 就表示从第 \(i\) 个「块」开始的 \(2^j\) 个块内的最大值
这样预处理完成之后就可以 \(O(1)\) 的时间得到一堆完整块的最大值
空间发复杂度上因为最多是有 \(\sqrt{n}\) 个「块」,所以可以接受
2.散块
一个区间的答案一定是有至多两个散块和若干个整块拼接出来的
原来暴力求散块的最大值方法不行,依然思考预处理。
可以发现答案一定是第一个散块的后缀最大值,和第二个散块的前缀最大值,可以提前预处理,这样就可以直接得到散块内的最大值


整体的复杂度已经可以接受,但是如果查询区间处于同一个块内,那就只能暴力求,依然有 \(O(\sqrt{n})\) 的复杂度,但是题目数据没有这么强,可以通过
但是这个代码的这个地方还能再优化

配图

图

code

#include<bits/stdc++.h>
#define int unsigned
#define fore(i,a,b) for( int i=(a); i<=(b); ++i)
#define repe(i,a,b) for( int i=(a); i>=(b); --i)
using namespace std;
namespace GenHelper
{
    unsigned z1,z2,z3,z4,b;
    unsigned rand_()
    {
    b=((z1<<6)^z1)>>13;
    z1=((z1&4294967294U)<<18)^b;
    b=((z2<<2)^z2)>>27;
    z2=((z2&4294967288U)<<2)^b;
    b=((z3<<13)^z3)>>21;
    z3=((z3&4294967280U)<<7)^b;
    b=((z4<<3)^z4)>>12;
    z4=((z4&4294967168U)<<13)^b;
    return (z1^z2^z3^z4);
    }
}
void srand(unsigned x)
{using namespace GenHelper;
z1=x; z2=(~x)^0x233333333U; z3=x^0x1234598766U; z4=(~x)+51;}
int read()
{
    using namespace GenHelper;
    int a=rand_()&32767;
    int b=rand_()&32767;
    return a*32768+b;
}
const int N=2e7+10;
const int qN=4e3+500;
int n,T,s;
int L[qN],R[qN],a[N];
int	q[N],p[N];
int pos[N],st[qN][20];
int logg[qN];
void init(){
	int len=sqrt(n);
	int num=(n+len-1)/len;
	for(int i=1;i<=num;i++){
		L[i]=(i-1)*len+1;
		R[i]=i*len;
	}
	if(R[num]>n)R[num]=n;
	for(int i=1;i<=num;i++){
		for(int j=L[i];j<=R[i];j++){
			pos[j]=i;
			st[i][0]=max(st[i][0],a[j]);
		}
		p[L[i]]=a[L[i]];
		q[R[i]]=a[R[i]];
		for(int j=L[i]+1;j<=R[i];j++){
			p[j]=max(p[j-1],a[j]);
		}
		for(int j=R[i]-1;j>=L[i];j--){
			q[j]=max(q[j+1],a[j]);
		}
	}
	logg[1]=0;
	for(int i=2;i<=num;i++)logg[i]=logg[i>>1]+1;
	for(int j=1;(1<<j)<=num;j++){
		for(int i=1;i+(1<<j)-1<=num;i++){
			st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
		}
	}
	return;
}
int query_st(int l,int r){
	if(l>r)return 0;
	int k=logg[r-l+1];
	return max(st[l][k],st[r-(1<<k)+1][k]);
}
unsigned long long ans_sum=0;
int query(int l,int r){
	if(l>r)swap(l,r);
	int x=pos[l],y=pos[r];
	int tmp=0;
	if(x==y){
		for(int i=l;i<=r;i++)tmp=max(tmp,a[i]);
	}
	else{
		tmp=query_st(x+1,y-1);
		tmp=max(tmp,max(q[l],p[r]));
	}
	return tmp;
}
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>T>>s;
	srand(s);
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	init();
	while(T--){
		int l=read()%n+1,r=read()%n+1;
		if(l>r)swap(l,r);
		ans_sum+=query(l,r);
	}
	cout<<ans_sum<<'\n';
	return 0;
}



P4879 ycz的妹子

唯一难点在于删除操作,要找到第x个没有被删掉的点
可以对每一个分块记录还在的点,对每个点记录是否还在
最后要找到第x个点,就从头遍历每一个分块,如果点数不足x,就减去,说明还在后面的分块,如过达到,就在分块内部找当前已经减剩下的第x个点
标记,更新分块信息
注意加入操作会加入新的点,提前把\(1e6\)个点预处理到每一个分块里,新加入的直接操作,注意标记成已存在不能用0表示是否存在,点权有可能为0

code

#include<bits/stdc++.h>
#define int long long
#define fore(i,a,b) for( int i=(a); i<=(b); ++i)
#define repe(i,a,b) for( int i=(a); i>=(b); --i)
using namespace std;
const int N=1e6+10;
int n,m;
int a[N],vis[N],siz[N],L[N],R[N],pos[N],sum[N];
void init(){
	int maxn=1e6;
	int len=sqrt(maxn);
	int num=(maxn+len-1)/len;
	for(int i=1;i<=num;i++){
		L[i]=(i-1)*len+1;
		R[i]=i*len;
	}
	R[num]=maxn;
	for(int i=1;i<=num;i++){
		for(int j=L[i];j<=R[i];j++){
			pos[j]=i;
			sum[i]+=a[j];
			siz[i]+=vis[j];
		}
	}
	return;
}
void change_c(int x,int y){
	if(vis[x]==0)return;
	sum[pos[x]]-=a[x];
	a[x]-=y;
	sum[pos[x]]+=a[x];
	return;
}
void change_i(int x,int y){
	if(vis[x]){
		sum[pos[x]]-=a[x];
		a[x]=y;
		sum[pos[x]]+=a[x];
	}
	else{
		vis[x]=1;
		sum[pos[x]]-=a[x];
		a[x]=y;
		sum[pos[x]]+=a[x];
		siz[pos[x]]+=1;
	}
	return;
}
void change_d(int x){
	int num=1;
	while(x-siz[num]>0){
		x-=siz[num];
		num++;
	}
	for(int i=L[num];i<=R[num];i++){
		if(x-vis[i]==0){
			if(x-vis[i]==0){
				siz[num]--;
				sum[num]-=a[i];
				vis[i]=a[i]=0;
				return;
			}
		}
		else{
			x-=vis[i];
		}
	}
	return;
}
int query(){
	int res=0;
	for(int i=1;i<=pos[1000000];i++){
		res+=sum[i];
	}
	return res;
}
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		vis[i]=1;
	}
	init();
	for(int i=1;i<=m;i++){
		char orr;
		int x,y;
		cin>>orr;
		if(orr=='C'){
			cin>>x>>y;
			change_c(x,y);
		}
		if(orr=='I'){
			cin>>x>>y;
			change_i(x,y);
		}
		if(orr=='D'){
			cin>>x;
			change_d(x);
		}
		if(orr=='Q'){
			cout<<query()<<'\n';
		}
	}
	return 0;
}



posted @ 2025-08-02 17:04  wmq2012  阅读(18)  评论(0)    收藏  举报