西安多校集训-分块学习笔记

前言

因bw用小脑想出来的课表,在 c层 根本学不到新知识,只能自学了qwq

1.分块思想

优雅的暴力,优雅的常数,优雅的思想。
对于一个序列,我们可以对其分成若干段等长的序列,每段序列称为一块。
然后对序列做操作时,可以对整块序列统一处理(就是操作的左右端点包含这一块序列),对散块序列暴力修改(就是操作的序列与这一块序列相交但不包含)。

2.分块复杂度

由上述可知,我们令原序列长度为 \(n\) ,块长为 \(len\) ,那么分块复杂度为 \(O(\frac{n}{len}+len)\)。由(高中的数学知识?)常识可知,当 \(len=\sqrt n\)\(\frac{n}{len}+len\) 最小,所以块长选取 \(\sqrt n\)时,理论复杂度最优为 \(O(n\sqrt n)\)当然出题人可能会特意的卡一卡。

3.分块的实现

①. 预处理

需要三个数组,\(L_x , R_x , belong_i\) 分别表示,第 \(x\) 块序列的左端点,右端点,以及原序列中的第 \(i\) 个点属于哪一块。
具体代码如下:

int n,m;
int len,tot,L[N],r[N],belong[N];
void build(){
	len=sqrt(n);
	tot=n/len;
	if(n%len) tot++;
	for(int i=1;i<=n;i++){
		belong[i]=(i-1)/len+1;
	}
	for(int i=1;i<=tot;i++){
		L[i]=(i-1)*len+1;
		R[i]=min(i*len,n);
	}
	return ;
}

②. 区间加法

顺应上述的分块思想,对于整块的修改打上 tag ,对于散块就暴力修改。可能还用到了一点点的标记永久化的思想?

void modify(int l,int r,int w){
	int x=belong[l],y=belong[r];
	if(x==y){
		for(int i=l;i<=r;i++){
			a[i]+=w;
			sum[x]+=w;
		}
		return ;
	}else{
		for(int i=l;i<=R[x];i++){
			a[i]+=w;
			sum[x]+=w;
		}
		for(int i=L[y];i<=r;i++){
			a[i]+=w;
			sum[y]+=w;
		}
		for(int i=x+1;i<y;i++){
			tag[i]+=w;
			sum[i]+=(R[i]-L[i]+1)*w;
		}
		return ;
	}
}

③. 区间求和

区间操作都很类似,只要注意一下计算时是否需要加上 tag 就行。

int query(int l,int r){
	int x=belong[l],y=belong[r];
	int ans=0;
	if(x==y){
		for(int i=l;i<=r;i++){
			ans+=a[i]+tag[x];
		}
		return ans;
	}else{
		for(int i=l;i<=R[x];i++){
			ans+=a[i]+tag[x];
		}
		for(int i=L[y];i<=r;i++){
			ans+=a[i]+tag[y];
		}
		for(int i=x+1;i<y;i++){
			ans+=sum[i];
		}
		return ans;
	}
}

fun fact:将上述代码拼起来就得到了 【模板】线段树 1 的代码,而且跑的比线段树快。(大雾)

4.区间多种运算

比如【模板】线段树 2
同线段树一样,依据 板1 改一改就行啦。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
const int M=1e6+10;
const int p=571373;
int n,m;
int a[N],tag[N],mul[N],sum[N];
int len,tot,L[N],R[N],belong[N];
void build(){
	len=sqrt(n);
	tot=n/len;
	if(n%len) tot++;
	for(int i=1;i<=n;i++){
		belong[i]=(i-1)/len+1;
		mul[belong[i]]=1;
		sum[belong[i]]+=a[i];
	}
	for(int i=1;i<=tot;i++){
		L[i]=(i-1)*len+1;
		R[i]=min(i*len,n);
	}
	return ;
}
void pushup(int pos){
	for(int i=L[pos];i<=R[pos];i++){
		a[i]=(a[i]*mul[pos]+tag[pos])%p;
	}
	mul[pos]=1;tag[pos]=0;
	return ;
}
void modify(int l,int r,int w){
	int x=belong[l],y=belong[r];
	if(x==y){
		for(int i=l;i<=r;i++){
			a[i]=(a[i]%p+w%p)%p;
			sum[x]=(sum[x]%p+w%p)%p;
		}
		return ;
	}else{
		pushup(x);
		for(int i=l;i<=R[x];i++){
			a[i]=(a[i]%p+w%p)%p;
			sum[x]=(sum[x]%p+w%p)%p;
		}
		pushup(y);
		for(int i=L[y];i<=r;i++){
			a[i]=(a[i]%p+w%p)%p;
			sum[y]=(sum[y]%p+w%p)%p;
		}
		for(int i=x+1;i<y;i++){
			tag[i]=(tag[i]%p+w%p)%p;
			sum[i]=(sum[i]+(R[i]-L[i]+1)%p*w%p)%p;
		}
		return ;
	}
}
void times(int l,int r,int w){
	int x=belong[l],y=belong[r];
	if(x==y){
		pushup(x);
		for(int i=l;i<=r;i++){
			a[i]=a[i]%p*w%p;
			sum[x]=(sum[x]%p+a[i]%p*(w-1)%p)%p;
		}
		return ;
	}else{
		pushup(x);
		for(int i=l;i<=R[x];i++){
			sum[x]=(sum[x]%p+a[i]%p*(w-1)%p)%p;
			a[i]=a[i]%p*w%p;
		}
		pushup(y);
		for(int i=L[y];i<=r;i++){
			sum[y]=(sum[y]%p+a[i]%p*(w-1)%p)%p;
			a[i]=a[i]%p*w%p;
		}
		for(int i=x+1;i<y;i++){
			tag[i]=tag[i]%p*w%p;
			mul[i]=mul[i]%p*w%p;
			sum[i]=sum[i]%p*w%p;
		}
	}
	return ;
}
int query(int l,int r){
	int x=belong[l],y=belong[r];
	int ans=0;
	if(x==y){
		for(int i=l;i<=r;i++){
			ans=(ans%p+a[i]%p*mul[x]%p+tag[x]%p)%p;
		}
		return ans%p;
	}else{
		for(int i=l;i<=R[x];i++){
			ans=(ans%p+a[i]%p*mul[x]%p+tag[x]%p)%p;
		}
		for(int i=L[y];i<=r;i++){
			ans=(ans%p+a[i]%p*mul[y]%p+tag[y]%p)%p;
		}
		for(int i=x+1;i<y;i++){
			ans=(ans%p+sum[i]%p)%p;
		}
		return ans%p;
	}
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int mod;
	cin>>n>>m>>mod;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build();
	for(int i=1;i<=m;i++){
		int op,l,r;
		cin>>op>>l>>r;
		if(op==2){
			int k;
			cin>>k;
			modify(l,r,k);
		}else if(op==3){
			cout<<query(l,r)%p<<'\n';
		}else{
			int k;
			cin>>k;
			times(l,r,k);
		}
	}
	return 0;
}

5. 区间其他操作

①. 教主的魔法

说人话:支持区间加法,查询区间内大于等于 k 的数的个数。
对于每一块都排好序,然后散块直接做就行了,对于整块,去二分查找后加上 \(R_i-x+1\) 即可。

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1e6+50;
int n,m;
int a[N],tag[N],rnk[N];
int L[N],R[N],len,tot;
int belong[N];
void segsort(int k){
	for(int i=L[k];i<=R[k];i++){
		rnk[i]=a[i];
	}
	sort(rnk+L[k],rnk+R[k]+1);
}
void modify(int l,int r,int w){
	int x=belong[l],y=belong[r];
	if(x==y){
		for(int i=l;i<=r;i++){
			a[i]+=w;
		}	
		segsort(x);
		return ;
	}
	for(int i=l;i<=R[x];i++){
		a[i]+=w;
	}
	segsort(x);
	for(int i=L[y];i<=r;i++){
		a[i]+=w;
	}
	segsort(y);
	for(int i=x+1;i<y;i++){
		tag[i]+=w;
	}
	return ;
}
int query(int l,int r,int c){
	int ans=0,x=belong[l],y=belong[r];
	if(x==y){
		for(int i=l;i<=r;i++){
			if(a[i]+tag[x]>=c) ans++;
		}
		return ans;
	}
	for(int i=l;i<=R[x];i++){
		if(a[i]+tag[x]>=c) ans++;
	}
	for(int i=L[y];i<=r;i++){
		if(a[i]+tag[y]>=c) ans++;
	}
	for(int i=x+1;i<y;i++){
		ans+=R[i]-(lower_bound(rnk+L[i],rnk+R[i]+1,c-tag[i])-rnk)+1;
	}
	return ans;
}
void build(){
	len=sqrt(n);
	tot=n/len;
	if(n%len) tot++;
	for(int i=1;i<=n;i++){
		belong[i]=(i-1)/len+1;
	}
	for(int i=1;i<=tot;i++){
		L[i]=(i-1)*len+1;
		R[i]=min(i*len,n);
        sort(rnk+L[i],rnk+R[i]+1);
	}
	return ;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		rnk[i]=a[i];
	}
	build();
	for(int i=1;i<=m;i++){
		char op;
		int l,r,c;
		cin>>op>>l>>r>>c;
		if(op=='A'){
			cout<<query(l,r,c)<<'\n';
		}else{
			modify(l,r,c);
		}
	}
	return 0;
}

②. 由乃打扑克

第一道 Ynoi 题!
Ynoi 题都是人话,好评!
思路继承教主的魔法,整块内排序,考虑怎么求得第 k 小值。
去二分这个第 k 小值,然后查这个值是否是第 k 小就行啦。

#include<iostream>
#include<algorithm>
#include<cmath>
#define int long long
using namespace std;
const int N=1e5+50;
const int inf=2e9+7;
int n,m;
int a[N],tag[N],rnk[N];
int len,tot;
int belong[N],L[N],R[N];
void build(){
	len=120;
	tot=n/len;
	if(n%len) tot++;
	for(int i=1;i<=n;i++){
		belong[i]=(i-1)/len+1;
	}
	for(int i=1;i<=tot;i++){
		L[i]=(i-1)*len+1;
		R[i]=min(i*len,n);
		sort(rnk+L[i],rnk+R[i]+1);
	}
	return ;
}
void Sort(int k){
	for(int i=L[k];i<=R[k];i++) rnk[i]=a[i];
	sort(rnk+L[k],rnk+R[k]+1);
	return ;
}
void modify(int l,int r,int c){
	int x=belong[l],y=belong[r];
	if(x==y){
		for(int i=l;i<=r;i++){
			a[i]+=c;
		}
		Sort(x);
		return ;
	}
	for(int i=l;i<=R[x];i++){
		a[i]+=c;
	}
	Sort(x);
	for(int i=L[y];i<=r;i++){
		a[i]+=c;
	}
	Sort(y);
	for(int i=x+1;i<y;i++){
		tag[i]+=c;
	}
	return ;
}
int check(int l,int r,int w){
	int x=belong[l],y=belong[r];
	int cnt=0;
	if(x==y){
		for(int i=l;i<=r;i++){
			if(a[i]+tag[x]<=w) cnt++;
		}
		return cnt;
	}
	for(int i=l;i<=R[x];i++){
		if(a[i]+tag[x]<=w) cnt++;
	}
	for(int i=L[y];i<=r;i++){
		if(a[i]+tag[y]<=w) cnt++;
	}
	for(int i=x+1;i<y;i++){
		if(rnk[L[i]]+tag[i]>w) continue;
		if(rnk[R[i]]+tag[i]<=w){
			cnt+=R[i]-L[i]+1;
			continue;
		}
		int ll=L[i],rr=R[i];
		while(ll<rr){
			int mid=((ll+rr)>>1)+1;
			if(rnk[mid]+tag[i]<=w) ll=mid;
			else rr=mid-1;
		}
		if(rnk[ll]+tag[i]<=w) cnt+=ll-L[i]+1;
	}
	return cnt;
}
int qmax(int l,int r){
	int x=belong[l],y=belong[r];
	int ans=-inf;
	if(x==y){
		for(int i=l;i<=r;i++){
			ans=max(ans,a[i]+tag[x]);
		}
		return ans;
	}
	for(int i=l;i<=R[x];i++) ans=max(ans,a[i]+tag[x]);
	for(int i=L[y];i<=r;i++) ans=max(ans,a[i]+tag[y]);
	for(int i=x+1;i<y;i++){
		ans=max(ans,rnk[R[i]]+tag[i]);
	}
	return ans;
}
int qmin(int l,int r){
	int x=belong[l],y=belong[r];
	int ans=inf;
	if(x==y){
		for(int i=l;i<=r;i++){
			ans=min(ans,a[i]+tag[x]);
		}
		return ans;
	}
	for(int i=l;i<=R[x];i++) ans=min(ans,a[i]+tag[x]);
	for(int i=L[y];i<=r;i++) ans=min(ans,a[i]+tag[y]);
	for(int i=x+1;i<y;i++){
		ans=min(ans,rnk[L[i]]+tag[i]);
	}
	return ans;
}
void query(int l,int r,int k){
	if(k<1 || k>r-l+1){
		cout<<-1<<'\n';
		return ;
	}
	int ll=qmin(l,r),rr=qmax(l,r);
	while(ll<=rr){
		int mid=(ll+rr)>>1;
		if(check(l,r,mid)<k){
			ll=mid+1;
		}else{
			rr=mid-1;
		}
	}
	cout<<ll<<'\n';
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		rnk[i]=a[i];
	}
	build();
	for(int i=1;i<=m;i++){
		int op,l,r,k;
		cin>>op>>l>>r>>k;
		if(op==1){
			query(l,r,k);
		}else{
			modify(l,r,k);
		}
	}
	return 0;
}
posted @ 2025-03-27 11:53  Tighnari  阅读(23)  评论(2)    收藏  举报