分块

数列分块入门 1

  • 给定一个长度为 n n n的序列,有 n n n个操作,区间加法和单点询问

  • 将序列分成 s q r t ( n ) sqrt(n) sqrt(n)个块

  • 对于一段区间修改,分两种情况讨论:

  • 如果修改区间在一个块中,则暴力修改 a [ i ] a[i] a[i]的值

  • 如果修改区间不在一个块中:
    对于最左边和最右边的块,暴力修改
    中间的整块维护一个块的 l a z y lazy lazy

  • 时间复杂度 O ( ( n / m ) + m ) O((n/m)+m) O((n/m)+m),根据均值不等式, m = s q r t ( n ) m=sqrt(n) m=sqrt(n)时最优

#include <bits/stdc++.h>
using namespace std;
#define maxn 50010

int n,a[maxn],L[maxn],R[maxn],t,pos[maxn],la[maxn];

inline int read_() {
	int x_=0,f_=1;char c_=getchar();
	while(c_<'0'||c_>'9') {if(c_=='-') f_=-1;c_=getchar();}
	while(c_>='0'&&c_<='9') {x_=(x_<<1)+(x_<<3)+c_-'0';c_=getchar();}
	return x_*f_;
}

inline void update_(int l,int r,int w) {
	int LL=pos[l],RR=pos[r];
	if(LL==RR) for(int i=l;i<=r;++i) a[i]+=w;
	else {
		for(int i=l;i<=R[LL];++i) a[i]+=w;
		for(int i=L[RR];i<=r;++i) a[i]+=w;
		for(int i=LL+1;i<=RR-1;++i) la[i]+=w;
	}
}

void readda_() {
	n=read_();
	for(int i=1;i<=n;++i) a[i]=read_();
	t=sqrt(n);//块的大小亦块数,t*t=n 
	for(int i=1;i<=t;++i) {
		L[i]=(i-1)*t+1;
		R[i]=i*t;
	}
	if(R[t]<n) {++t;L[t]=R[t-1]+1;R[t]=n;} 
	for(int i=1;i<=t;++i) {
		for(int j=L[i];j<=R[i];++j) {
			pos[j]=i;//每个点属于哪个块 
		}
	}
	for(int i=1;i<=n;++i) {
		int pd=read_(),l=read_(),r=read_(),c=read_();
		if(!pd) update_(l,r,c);
		else printf("%d\n",a[r]+la[pos[r]]);
	}
}

int main() {
	freopen("a.txt","r",stdin);
	readda_();
	return 0;
}

数列分块入门 2

  • 给出一个长为 n n n的数列,以及 n n n个操作,操作涉及区间加法,询问区间内小于某个值 x x x的元素个数。

  • 要快速查询一个区间小于 x x x的数有多少,在区间有序的情况下我们可以使用二分 l o w e r lower lower_ b o u n d bound bound

  • 维护出块之后,给每个块内排序,使块内元素有序

  • 考虑修改:
    如果一整个块都加上一个数,不影响块内元素的相对大小
    如果修改一个块内的部分的数,则有可能会影响相对大小,在修改完之后,重新排序

  • 考虑询问:
    如果询问一整个块的话使用二分
    否则暴力询问

#include <bits/stdc++.h>
using namespace std;
#define maxn 50010
#define maxt 225

int n,a[maxn],L[maxt],R[maxt],pos[maxn],t,la[maxt];
vector <int >e[maxt];

inline int read_() {
	int x_=0,f_=1;char c_=getchar();
	while(c_<'0'||c_>'9') {if(c_=='-') f_=-1;c_=getchar();}
	while(c_>='0'&&c_<='9') {x_=(x_<<1)+(x_<<3)+c_-'0';c_=getchar();}
	return x_*f_;
}

inline void resert_(int x) {
	e[x].clear();
	for(int i=L[x];i<=R[x];++i) e[x].push_back(a[i]);
	sort(e[x].begin(),e[x].end());
}

inline void update_(int l,int r,int w) {
	int LL=pos[l],RR=pos[r];
	if(LL==RR) {
		for(int i=l;i<=r;++i) a[i]+=w;
		resert_(LL); 
	}
	else {
		for(int i=l;i<=R[LL];++i) a[i]+=w;
		resert_(LL);
		for(int i=L[RR];i<=r;++i) a[i]+=w;
		resert_(RR);
		for(int i=LL+1;i<=RR-1;++i) la[i]+=w;
	}
}

inline int query_(int l,int r,int w) {
	int LL=pos[l],RR=pos[r];
	int ans=0;
	if(LL==RR) {
		for(int i=l;i<=r;++i) {
			if(a[i]+la[pos[i]]<w) ++ans;
		}
	}
	else {
		for(int i=l;i<=R[LL];++i) {
			if(a[i]+la[pos[i]]<w) ++ans;
		} 
		for(int i=L[RR];i<=r;++i) {
			if(a[i]+la[pos[i]]<w) ++ans;
		}
		for(int i=LL+1;i<=RR-1;++i) {
			ans+=lower_bound(e[i].begin(),e[i].end(),w-la[i])-e[i].begin();
		}
	}
	return ans;
}

void readda_() {
	n=read_();
	for(int i=1;i<=n;++i) a[i]=read_();
	t=sqrt(n);
	for(int i=1;i<=t;++i) {
		L[i]=(i-1)*t+1;
		R[i]=i*t;
	}
	if(R[t]<n) {++t;L[t]=R[t-1]+1;R[t]=n;}
	for(int i=1;i<=t;++i) {
		for(int j=L[i];j<=R[i];++j) {
			pos[j]=i;
			e[i].push_back(a[j]);
		}
		sort(e[i].begin(),e[i].end());
	}
	for(int i=1;i<=n;++i) {
		int pd=read_(),l=read_(),r=read_(),c=read_();
		if(!pd) update_(l,r,c);
		else printf("%d\n",query_(l,r,c*c));
	}
}

int main() {
	freopen("a.txt","r",stdin);
	readda_();
	return 0;
}

数列分块入门 3

  • 给出一个长为 n n n的数列,以及 n n n个操作,操作涉及区间加法,询问区间内小于某个值 x x x的前驱(比其小的最大元素)。
  • 和上一题差不多,使块内的元素单调,可以使用二分查询 x x x的位置, x x x的前驱就是其前面那个
  • 我们开个 v i s [ i ] vis[i] vis[i]表示 i i i号块是否被暴力修改,在查询时再去维护其单调性,不用修改了就去维护,有可能询问区间不涉及暴力修改区间
#include <bits/stdc++.h>
using namespace std;
#define maxn 100010
#define maxt 330

int t,n,a[maxn],L[maxt],R[maxt],pos[maxn],la[maxt];
bool vis[maxt];
vector <int > e[maxt];

inline int read_() {
   int x_=0,f_=1;char c_=getchar();
   while(c_<'0'||c_>'9') {if(c_=='-') f_=-1;c_=getchar();}
   while(c_>='0'&&c_<='9') {x_=(x_<<1)+(x_<<3)+c_-'0';c_=getchar();}
   return x_*f_;
}

inline void resert_(int x) {
   e[x].clear();
   for(int i=L[x];i<=R[x];++i) e[x].push_back(a[i]);
   sort(e[x].begin(),e[x].end());
   vis[x]=0;
} 
inline void update_(int l,int r,int w) {
   int LL=pos[l],RR=pos[r];
   if(LL==RR) {
   	for(int i=l;i<=r;++i) a[i]+=w;
   	vis[LL]=1;
   }
   else {
   	for(int i=l;i<=R[LL];++i) a[i]+=w;
   	vis[LL]=1;
   	for(int i=L[RR];i<=r;++i) a[i]+=w;
   	vis[RR]=1;
   	for(int i=LL+1;i<=RR-1;++i) la[i]+=w;
   }
} 

inline int query_(int l,int r,int w) {
   int LL=pos[l],RR=pos[r];
   if(LL==RR) {
   	int ans=-1;
   	for(int i=l;i<=r;++i) {
   		if(a[i]+la[pos[i]]<w) {
   			ans=max(ans,a[i]+la[pos[i]]);
   		}
   	}
   	return ans;
   }
   else {
   	int ans=-1;
   	for(int i=l;i<=R[LL];++i) {
   		if(a[i]+la[pos[i]]<w) {
   			ans=max(ans,a[i]+la[pos[i]]);
   		} 
   	}
   	for(int i=L[RR];i<=r;++i) {
   		if(a[i]+la[pos[i]]<w) {
   			ans=max(ans,a[i]+la[pos[i]]);
   		}
   	}
   	for(int i=LL+1;i<=RR-1;++i) {
   		if(vis[i]) resert_(i);
   		int AKIOI=lower_bound(e[i].begin(),e[i].end(),w-la[i])-e[i].begin();
   		if(!AKIOI) continue;
   		ans=max(ans,e[i][AKIOI-1]+la[i]);
   	} 
   	return ans;
   }
}

void readda_() {
   n=read_();
   for(int i=1;i<=n;++i) a[i]=read_();
   t=sqrt(n);
   for(int i=1;i<=t;++i) {
   	L[i]=(i-1)*t+1;
   	R[i]=i*t;
   }
   if(R[t]<n) {++t;L[t]=R[t-1]+1;R[t]=n;}
   for(int i=1;i<=t;++i) {
   	for(int j=L[i];j<=R[i];++j) {
   		pos[j]=i;
   		e[i].push_back(a[j]);
   	}
   	sort(e[i].begin(),e[i].end());
   }
   for(int i=1;i<=n;++i) {
   	int pd=read_(),l=read_(),r=read_(),c=read_();
   	if(!pd) update_(l,r,c);
   	else printf("%d\n",query_(l,r,c));
   }
}

int main() {
   freopen("a.txt","r",stdin);
   readda_();
   return 0;
}
  • 当然,这个题想表达的思想是块内又可以用另一种数据结构来实现,所以块内部可以用 s e t set set来实现
#include <bits/stdc++.h>
using namespace std;
#define maxn 100010
#define maxt 330

int n,L[maxt],R[maxt],la[maxt],a[maxn],t,pos[maxn];
set <int > e[maxt];

inline int read_() {
	int x_=0,f_=1;char c_=getchar();
	while(c_<'0'||c_>'9') {if(c_=='-') f_=-1;c_=getchar();}
	while(c_>='0'&&c_<='9') {x_=(x_<<1)+(x_<<3)+c_-'0';c_=getchar();}
	return x_*f_;
}

inline void update_(int l,int r,int w) {
	int LL=pos[l],RR=pos[r];
	if(LL==RR) {
		for(int i=l;i<=r;++i) {
			e[LL].erase(a[i]);
			a[i]+=w;
			e[LL].insert(a[i]);
		} 
	}
	else {
		for(int i=l;i<=R[LL];++i) {
			e[LL].erase(a[i]);
			a[i]+=w;
			e[LL].insert(a[i]);
		} 
		for(int i=L[RR];i<=r;++i) {
			e[RR].erase(a[i]);
			a[i]+=w;
			e[RR].insert(a[i]);
		}
		for(int i=LL+1;i<=RR-1;++i) la[i]+=w;
	}
}

inline int query_(int l,int r,int w) {
	int LL=pos[l],RR=pos[r];
	if(LL==RR) {
		int ans=-1,AKIOI;
		for(int i=l;i<=r;++i) {
			AKIOI=a[i]+la[LL];
			if(AKIOI<w) ans=max(ans,AKIOI); 
		}
		return ans;
	}
	else {
		int ans=-1,AKIOI;
		for(int i=l;i<=R[LL];++i) {
			AKIOI=a[i]+la[LL];
			if(AKIOI<w) ans=max(ans,AKIOI);
		} 
		for(int i=L[RR];i<=r;++i) {
			AKIOI=a[i]+la[RR];
			if(AKIOI<w) ans=max(ans,AKIOI);
		}
		for(int i=LL+1;i<=RR-1;++i) {
			set<int> :: iterator it=e[i].lower_bound(w-la[i]); 
			if(it==e[i].begin()) continue;
			--it;
			ans=max(ans,*it+la[i]);
     	}
     	return ans;
	}
} 

void readda_() {
	n=read_();
	for(int i=1;i<=n;++i) a[i]=read_();
	t=sqrt(n);
	for(int i=1;i<=t;++i) {
		L[i]=(i-1)*t+1;
		R[i]=i*t;
	}
	if(R[t]<n) {++t;L[t]=R[t-1]+1;R[t]=n;}
	for(int i=1;i<=t;++i) {
		for(int j=L[i];j<=R[i];++j) {
			pos[j]=i;
			e[i].insert(a[j]);
		}
	}
	for(int i=1;i<=n;++i) {
		int pd=read_(),l=read_(),r=read_(),c=read_();
		if(!pd) update_(l,r,c);
		else printf("%d\n",query_(l,r,c));
	}
}

int main() {
	freopen("a.txt","r",stdin);
	readda_();
	return 0;
}

数列分块入门 4

  • 给出一个长为 n n n的数列,以及 n n n个操作,操作涉及区间加法,区间求和。
  • 每个块维护一个块的总和 s u m [ i ] sum[i] sum[i]
  • 修改时,不完整块暴力修改 a 和 s u m a和sum asum,完整块修改 l a z y lazy lazy
  • 询问时,不完整块暴力询问,完整块询问和,注意加上 l a z y ∗ 区 间 长 度 lazy*区间长度 lazy
  • 要开 l o n g long long l o n g long long
#include <bits/stdc++.h>
using namespace std;
#define maxn 50010
#define maxt 240

int n,L[maxt],R[maxt],pos[maxn],t;
long long a[maxn],sum[maxt],la[maxt];

inline long long read_() {
	long long x_=0,f_=1;char c_=getchar();
	while(c_<'0'||c_>'9') {if(c_=='-') f_=-1;c_=getchar();}
	while(c_>='0'&&c_<='9') {x_=(x_<<1)+(x_<<3)+c_-'0';c_=getchar();}
	return x_*f_;
}

inline void update_(int l,int r,int w) {
	int LL=pos[l],RR=pos[r];
	if(LL==RR) {
		for(int i=l;i<=r;++i) {
			a[i]+=w;
			sum[LL]+=w;
		} 
	}
	else {
		for(int i=l;i<=R[LL];++i) {
			a[i]+=w;
			sum[LL]+=w;
		}
		for(int i=L[RR];i<=r;++i) {
			a[i]+=w;
			sum[RR]+=w;
		}
		for(int i=LL+1;i<=RR-1;++i) la[i]+=w;
	}
}

inline long long query_(int l,int r) {
	int LL=pos[l],RR=pos[r];
	long long ans=0;
	if(LL==RR) {
		for(int i=l;i<=r;++i) ans+=a[i];	
		ans+=(r-l+1)*la[LL];
	}
	else {
		for(int i=l;i<=R[LL];++i) ans+=a[i];
		ans+=(R[LL]-l+1)*la[LL];
		for(int i=L[RR];i<=r;++i) ans+=a[i];
		ans+=(r-L[RR]+1)*la[RR];
		for(int i=LL+1;i<=RR-1;++i) {
			ans+=sum[i];
			ans+=(R[i]-L[i]+1)*la[i];
		}
	}
	return ans;
}

void readda_() {
	n=read_();
	for(int i=1;i<=n;++i) a[i]=read_();
	t=sqrt(n);
	for(int i=1;i<=t;++i) {
		L[i]=(i-1)*t+1;
		R[i]=i*t;
	}
	if(R[t]<n) {++t;L[t]=R[t-1]+1;R[t]=n;}
	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 pd=read_(),l=read_(),r=read_(),c=read_();
		if(!pd) update_(l,r,c);
		else printf("%lld\n",query_(l,r)%(c+1));
	} 
}

int main() {
	freopen("a.txt","r",stdin);
	readda_();
	return 0;
}

数列分块入门 5

  • 给出一个长为 n n n的数列 ,以及 n n n个操作,操作涉及区间开方,区间求和。
  • 修改:不是完整的块暴力修改,对于完整的块?我们也只能暴力修改,但如果块内元素都小于等于 1 1 1,就不用去修改了,而一个很大的数开方几次就到 1 1 1了,所以复杂度是能承受的。
  • 询问:不完整的块暴力询问,完整的块询问块的和
#include <bits/stdc++.h>
using namespace std;
#define maxn 50010
#define maxt 240

int n,a[maxn],L[maxt],R[maxt],sum[maxt],pos[maxn],t;
bool flag[maxt];

inline int read_() {
   int x_=0,f_=1;char c_=getchar();
   while(c_<'0'||c_>'9') {if(c_=='-') f_=-1;c_=getchar();}
   while(c_>='0'&&c_<='9') {x_=(x_<<1)+(x_<<3)+c_-'0';c_=getchar();}
   return x_*f_;
}

inline void solve_(int x) {
   if(flag[x]) return ;
   flag[x]=1;
   for(int i=L[x];i<=R[x];++i) {
   	sum[x]-=a[i];
   	a[i]=sqrt(a[i]);
   	sum[x]+=a[i];
   	if(a[i]>1) flag[x]=0; 
   }
}

inline void update_(int l,int r,int w) {
   int LL=pos[l],RR=pos[r];
   if(LL==RR) {
   	for(int i=l;i<=r;++i) {
   		sum[LL]-=a[i];
   		a[i]=sqrt(a[i]);
   		sum[LL]+=a[i];
   	}
   }
   else {
   	for(int i=l;i<=R[LL];++i){
   		sum[LL]-=a[i];
   		a[i]=sqrt(a[i]);
   		sum[LL]+=a[i];
   	}
   	for(int i=L[RR];i<=r;++i) {
   		sum[RR]-=a[i];
   		a[i]=sqrt(a[i]);
   		sum[RR]+=a[i];
   	}
   	for(int i=LL+1;i<=RR-1;++i) solve_(i);
   }
}

inline int query_(int l,int r) {
   int LL=pos[l],RR=pos[r];
   int ans=0;
   if(LL==RR) {
   	for(int i=l;i<=r;++i) ans+=a[i];
   }
   else {
   	for(int i=l;i<=R[LL];++i) ans+=a[i];
   	for(int i=L[RR];i<=r;++i) ans+=a[i];
   	for(int i=LL+1;i<=RR-1;++i) ans+=sum[i];
   }
   return ans;
}

void readda_() {
   n=read_();
   for(int i=1;i<=n;++i) a[i]=read_();
   t=sqrt(n);
   for(int i=1;i<=t;++i) {
   	L[i]=(i-1)*t+1;
   	R[i]=i*t;
   }
   if(R[t]<n) {++t;L[t]=R[t-1]+1;R[t]=n;}
   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 pd=read_(),l=read_(),r=read_(),c=read_();
   	if(!pd) update_(l,r,c);
   	else printf("%d\n",query_(l,r));
   }
}

int main() {
   freopen("a.txt","r",stdin);
   readda_();
   return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值