今天學習了一個算法(這個應該叫做算法吧?)叫做分塊(和莫隊,但是莫隊還沒有搞懂,搞懂再來寫吧)
聽起來很高級,蒟蒻表示瑟瑟發抖。但是學完發現怎么那么像是一種變相的暴力呢。
分塊思想:假如你要處理一個很長區間上的問題,并且有很多個查詢(以及修改),暴力顯然已經不可行了。但是暴力效率低下的主要原因是因為他沒有很好的利用每次處理后得到的信息而選擇重新處理,而進行優化的關鍵就是如何有效的儲存每次處理后的信息。對于線段樹來講是將這個區間進行好多次二分變成一個樹進行儲存,大區間就儲存著每次處理后的信息從而提升效率,對于樹狀數組來說它同樣是將區間分成了許多小部分,用的不是二分而是對二進制的一種應用形成的樹狀結構。而我們能不能什么高級的數據結構都不使用來解決這種問題呢?答案就是樸素的分塊思想——如果區間比較大,就分成一個一個小段來處理,用一個數組來保存每一個小段的信息,然后在具體詢問的時候如果詢問的區間大于我們分的塊就能夠使用我們保存過的數據提高效率,如果小于就直接暴力應該也不會很大。如果大于我們分的塊但又不是整塊怎么辦呢?就將兩端不是整塊的進行暴力,應該也不會很大(至少可以接受)
這里附一道比較有趣的分塊題(我做的第一道分塊題):BZOJ2002
題意大概是你有一只綿羊(不要問我為什么是只羊而不是兔子),然后它可以在許多彈簧上跳,現在給你在每個彈簧上能夠向后跳多遠,問你它跳多少次后綿羊就掉下去。而且還會修改彈簧的彈力。
對于這個問題很容易有一個暴力的思想就是我們對于每次詢問就直接讓他跳就好了,每次修改就改就好了,顯然會爆。
有一種比較巧妙的從后往前跳一邊然后儲存這個信息以后就能直接使用(從后往前是因為他是向后跳的,所以只會被后面的值影響),但是每次修改又會變成一個比較復雜的問題。
所以我們折中的使用分塊,就是將原來的大區間分成許多小區間(一般來講是sqrt(n)不要問我為什么,我也不知道,好像是什么基本不等式推出來比較好),然后每次更新小區間的值,查詢的時候每次用小區間的值。
具體的做法是用兩個數組,一個儲存跳出當前塊需要多少步(求答案的時候直接加起來),一個儲存跳出這個塊后會跳到下個塊的什么位置。從后往前進行初始化。然后對于每次更新只在塊內更新,對塊外的沒有影響。
詳細看注釋:
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
#define belong(x) ((x-1)/block+1)
using namespace std;const int maxn=200005;
int n,m,block;
int a[maxn],pos[maxn],step[maxn];int ask(int x)
{int ans=0;while(x!=-1){ans+=step[x];x=pos[x];}return ans;
}
int main()
{memset(a,0,sizeof(a));memset(pos,0,sizeof(pos));memset(step,0,sizeof(step));scanf("%d",&n);block=(int)sqrt(n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}for(int i=n;i>0;i--) //從后往前初始化{if(i+a[i]>n){pos[i]=-1; step[i]=1; //如果直接跳出整個區間了標記位置為-1}else if(i+a[i]>belong(i)*block){pos[i]=i+a[i]; step[i]=1; //記錄跳到下一個塊的什么位置}else{ //如果沒有跳出去就會一直跳到i+a[i]跳出去的位置,用的步數等于i+a[i]用的步數+1pos[i]=pos[i+a[i]]; step[i]=step[i+a[i]]+1;}}scanf("%d",&m);int cmd,u,v;for(int i=0;i<m;i++){scanf("%d",&cmd);if(cmd==1){scanf("%d",&u);u++;printf("%d\n",ask(u)); }else if(cmd==2){scanf("%d%d",&u,&v);u++; //我們這里是從1開始的,而問題是從0開始的,請注意a[u]=v;int left=(belong(u)-1)*block;for(int i=u;i>left;i--){if(i+a[i]>n){pos[i]=-1;step[i]=1;}else if(i+a[i]>belong(i)*block){pos[i]=i+a[i];step[i]=1;}else{pos[i]=pos[i+a[i]];step[i]=step[i+a[i]]+1;}}}}return 0;
}
就醬 ^_ ^