分桶法:把一排物品或者平面分成桶,每个桶分别维护自己内部的信息,已到达高效计算的目的的方法。
其中,平方分割是把排成一排的n个元素每√n个分在一个桶内进行维护的方法的统称。这样的方法可以使对区间的操作复杂度降至O(√n)。
以RMQ为例:
基于平方分割的RMQ
给定一个数列a1,a2,a3,a4,a5,a6,....,an,目标是在O(√n)浮渣度内实现以下两个功能
- 给定s,t,求as,as+1,as+2,...,at的最小值。
- 给定i,x,把ai的值变成x。
基于平方分割的RMQ预处理
令len=(int)(√n),把a中的元素每len个分成一个桶,并且计算出每个桶内的最值。
基于平方分割的RMQ查询
如下图所示,查询
- 如果桶完全包含在区间内,则查询桶内的最小值。
- 如果元素所在的桶不完全被包含包含,则逐个检查最值。
它们的最值就是区间的最值了。
基于平方分割的RMQ的值的更新
在更新元素的值时,只需要更新该元素所在的桶的最值。
基于平方分割的RMQ的复杂度
在更新时,因为每个桶内有len个元素,所以复杂度是O(len)=√n
而在查询时
- 完全包含在区间内的桶的个数是O(n/len)
- 所在的桶不被区间完全包含的元素个数是O(len)
因为len=O(√n),所以操作复杂度是O(n/len+len)=O(√n+√n)=O(√n)
附上POJ 3246.Balanced Lineup的平方分割的RMQ写法
代码:

#include <iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<map> #include<queue> #include<stack> #include<vector> #include<set> using namespace std; #define PI acos(-1.0) typedef long long ll; typedef pair<int,string> P; const int maxn=1e5+100,maxm=1e5+100,inf=0x3f3f3f3f,mod=1e9+7; const ll INF=1e13+7; priority_queue<P,vector<P>,greater<P> >q; struct edge { int from,to; int cost; }; ///分桶法+平方分割解决RMQ问题 struct node { int l,r; int mmin,mmax; } sign[maxn]; int a[maxn]; int main() { int n,q; scanf("%d%d",&n,&q); int x=(int)(sqrt(n)); int t=0; for(int i=1; i<=n; i++) { scanf("%d",&a[i]); if(i%x==1) t++,sign[t].l=i,sign[t].r=i,sign[t].mmax=0,sign[t].mmin=inf; sign[t].mmin=min(sign[t].mmin,a[i]); sign[t].mmax=max(sign[t].mmax,a[i]); sign[t].r=i; } while(q--) { int l,r; scanf("%d%d",&l,&r); int mmax=0,mmin=inf; for(int i=1; i<=t; i++) { if(l>sign[i].r) continue; if(l>sign[i].l) { for(int j=l; j<=sign[i].r&&j<=r; j++) mmax=max(mmax,a[j]),mmin=min(mmin,a[j]); } else if(l<=sign[i].l&&sign[i].r<=r) mmax=max(mmax,sign[i].mmax),mmin=min(mmin,sign[i].mmin); else if(r<sign[i].r) { for(int j=sign[i].l; j<=r; j++) mmax=max(mmax,a[j]),mmin=min(mmin,a[j]); } if(r<=sign[i].r) break; } cout<<mmax-mmin<<endl; } return 0; }