學莫隊之前先看了看分塊,,總結一下的話感覺還是一個暴力的算法
一、什麼時候可以用到分塊呢?
如果問,一個數列,給定m次詢問,每次問一個區間內的和是多少?
很明顯可以用前綴和來解決
那麼如果問還要對隨機一個區間進行加或減之類的修改呢?
很容易想到樹狀數組和線段樹來解決
再問,如果在加上問一個區間大於等於k的數有幾個呢?
這個時候就需要這個‘暴力’的方法出場了,我們可以用分塊解決。
二、什麼是分塊?
一個有n個數的數列,將它平均分爲sqrt(n)或sqrt(n)+1塊,這裏分成多少塊取決於sqrt(n)是否是一個整數。
然後每次更新時對於一個整塊可以直接進行更新,常用的是用一個atag標記數組標記,而對於那些不是一個整塊
的範圍,直接暴力枚舉就可以了。
查詢的時候也是這樣,對於整塊通常使用二分查詢,不是整塊的依然是暴力查詢。
那麼爲什麼要分成sqrt(n)塊呢?
因爲如果分的塊數太多,累加塊的時候就會變慢,而如果分的太少,每次對於一個塊的累加就會變慢,綜上,分成sqrt(n)
是最理想的分法。
分塊的時間複雜度是O(sqrt(n)),爲什麼呢?
對於整個塊的累加是O(sqrt(n)),而對於散的塊累加,因爲散的塊的數不會超過sqrt(n),所以時間複雜度最多依然是sqrt(n)。
三、塊的建立
因爲查詢和更新要因題而異
這裏就說一下塊的建立
void build()
{
blo=sqrt(n); //blo爲每一塊有多少個
for(int i=1;i<=n;i++){
b[i]=a[i]; //b數組用來排序原數組,以便最後二分查找時用
bl[i]=(i-1)/blo+1; //bl[i]表示第i個屬於第幾塊
}
for(int i=1;i<=bl[n];i++){ //bl[n]表示的是最後一個元素在第幾個塊裏,即一共分成了多少塊
l[i]=(i-1)*blo+1; //l[i]表示第i塊的左端點是第幾個元素
r[i]=blo*i; //r[i]表示第i塊的右端點是第幾個元素
}
r[bl[n]]=n; //最後一個塊的右端點必然是n
for(int i=1;i<=bl[n];i++)
sort(b+l[i],b+r[i]+1);
}
前面也提到了分塊很“暴力”,,所以如果遇到大的數據範圍就不要考慮它了......
這裏給出一個例題洛谷p2801
如果還有對更新和查詢操作有疑問的可以結合這個例題看一下
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e6+10;
int a[N],b[N],l[N],r[N],bl[N],blo,atag[N],n,m; //bl[i]表示第i個屬於第幾塊
void build()
{
blo=sqrt(n); //blo爲每一塊有多少個
for(int i=1;i<=n;i++){
b[i]=a[i]; //b數組用來排序原數組,以便最後二分查找時用
bl[i]=(i-1)/blo+1; //bl[i]表示第i個屬於第幾塊
}
for(int i=1;i<=bl[n];i++){ //bl[n]表示的是最後一個元素在第幾個塊裏,即一共分成了多少塊
l[i]=(i-1)*blo+1; //l[i]表示第i塊的左端點是第幾個元素
r[i]=blo*i; //r[i]表示第i塊的右端點是第幾個元素
}
r[bl[n]]=n; //最後一個塊的右端點必然是n
for(int i=1;i<=bl[n];i++)
sort(b+l[i],b+r[i]+1);
}
void reset(int x) //每次更新完重新排序
{
for(int i=l[x];i<=r[x];++i)
b[i]=a[i];
sort(b+l[x],b+r[x]+1);
}
void update(int x,int y,int z)
{
if(bl[x]==bl[y]){ //如果在一個塊中,直接處理
for(int i=x;i<=y;++i)
a[i]+=z;
reset(bl[x]);
return ;
}
for(int i=x;i<=r[bl[x]];i++) //否則暴力處理不是整塊的,就是x所在的那個塊
a[i]+=z;
reset(bl[x]);
for(int i=l[bl[y]];i<=y;i++) //暴力處理不是整塊的,也就是y所在的那個塊
a[i]+=z;
reset(bl[y]);
for(int i=bl[x]+1;i<=bl[y]-1;i++) //其餘的整塊在處理,累加到atag數組中
atag[i]+=z;
}
int Find(int x,int z) //二分
{
int ll=l[x],rr=r[x];
while(ll<=rr){
int mid=(ll+rr)>>1;
if(b[mid]<z)
ll=mid+1;
else rr=mid-1;
}
return r[x]-ll+1;
}
int query(int x,int y,int z)
{
int ans=0;
if(bl[x]==bl[y]){ //如果在一個塊中,直接查詢
for(int i=x;i<=y;i++)
if(a[i]+atag[bl[i]]>=z)
ans++;
return ans;
}
for(int i=x;i<=r[bl[x]];i++) //否則依然是先處理散塊
if(a[i]+atag[bl[i]]>=z)
ans++;
for(int i=l[bl[y]];i<=y;i++)
if(a[i]+atag[bl[i]]>=z)
ans++;
for(int i=bl[x]+1;i<=bl[y]-1;i++) //最後對整塊處理
ans+=Find(i,z-atag[i]);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build();
int x,y,z;
char c;
for(int i=1;i<=m;i++){
cin>>c;
scanf("%d%d%d",&x,&y,&z);
if(c=='M')
update(x,y,z);
else printf("%d\n",query(x,y,z));
}
return 0;
}