分塊(真的是優雅的暴力)
M - 一道普通題1
給出一個長為 nn 的數列,以及 nn 個操作,操作涉及區間加法,詢問區間內小於某個值 xx 的前驅(比其小的最大元素)。
Input
第一行輸入一個數字 n,1≤n≤100000n,1≤n≤100000。
第二行輸入 nn 個非負整數,第 ii個數字為 ai(0≤ai≤109)ai(0≤ai≤109)以空格隔開。
接下來輸入nn 行詢問,每行輸入四個數字 opt、l、r、c(c>0)opt、l、r、c(c>0),以空格隔開。
若 oopt=0opt=0,表示將位於[l,r][l,r]的之間的數字都加 cc。
若 opt=1opt=1,表示詢問[l,r][l,r]中 cc 的前驅的值(不存在則輸出 −1−1)。
Output
對於每次詢問,輸出一行一個數字表示答案。
保證所有資料在int
範圍內
Sample Input
4 1 2 2 3 0 1 3 1 1 1 4 4 0 1 2 2 1 1 2 4
Sample Output
3 -1
Hint
最初一看到這個題的這種格式就準備用樹狀陣列來做的,可是做到詢問前驅的時候就發現做不好了,然後就瞄了一下了學長的題解,才發現要用分塊做,分塊呢我只知道它的模板,~~~(我真的好菜哎^^),然後我就套模板一寫,可提交就T了,中間又錯了好多次,最後又看了看學長的程式碼,學長是用的vector來存每一塊的數,左右兩端殘塊暴力處理,中間的整塊(如果有的話)一塊塊的處理,這時更新的時候就沒有對原陣列直接+val,而是維護一個lazy陣列,表示每一塊要加的數是多少,比較的時候用val-lazy[i]與原陣列比較,如果有比它小的最大的數則存在字首,且為找到的原陣列的那個數+lazy[i],否則就沒有。。。。。。。最後明白這才叫分塊嘛,中間一塊一塊的處理,像我最初中間還是一個一個點處理,那分塊意義何在。。。。。。。
AC程式碼:
#include<iostream> #include<cstring> #include<algorithm> #include<set> #include<cmath> #include<cstdio> #include<vector> using namespace std; const int maxn=100001; int belong[maxn],l[maxn],r[maxn],b[maxn]; int lazy[maxn]; int block,num; int n; vector<int>s[1010]; void build() { block=(int)sqrt(n); num=n/block; if(n%block) num++; for(int i=1;i<=num;i++) l[i]=(i-1)*block+1,r[i]=i*block; r[num]=n;// for(int i=1;i<=n;i++) { scanf("%d",&b[i]); belong[i]=(i-1)/block+1; s[belong[i]].push_back(b[i]); } for(int i=1;i<=belong[n];i++) sort(s[i].begin(),s[i].end()); } void update(int ll,int rr,int val) { int first=belong[ll]; int last=belong[rr]; for(int i=ll;i<=min(belong[ll]*block,rr);i++) b[i]+=val; s[belong[ll]].clear(); for(int i=(belong[ll]-1)*block+1;i<=min(belong[ll]*block,n);i++) s[belong[ll]].push_back(b[i]); sort(s[belong[ll]].begin(),s[belong[ll]].end());//左邊殘塊 if(belong[ll]==belong[rr]) return;//如果只有一個殘塊修改完畢 for(int i=(belong[rr]-1)*block;i<=rr;i++) b[i]+=val; s[belong[rr]].clear(); for(int i=(belong[rr]-1)*block+1;i<=min(belong[rr]*block,n);i++) s[belong[rr]].push_back(b[i]); sort(s[belong[rr]].begin(),s[belong[rr]].end());//右邊殘塊 for(int i=first+1;i<=last-1;i++) lazy[i]+=val;//中間的整塊用lazy維護而不對原陣列直接+ } int query(int ll,int rr,int val) { int first=belong[ll]; int last=belong[rr]; int _max=-1; for(int i=ll;i<=min(r[belong[ll]],rr);i++) if(b[i]+lazy[belong[i]]<val) _max=max(_max,b[i]+lazy[belong[i]]); if(first!=last) { for(int i=l[belong[rr]];i<=rr;i++) if(b[i]+lazy[belong[i]]<val) _max=max(b[i]+lazy[belong[i]],_max); } for(int i=first+1;i<=last-1;i++) { auto it=lower_bound(s[i].begin(),s[i].end(),val-lazy[i]); if(it==s[i].begin()) continue; --it; _max=max(_max,*it+lazy[i]); } return _max; } int main() { //ios::sync_with_stdio(0),cin.tie(0); while(~scanf("%d",&n)){ build(); int m=n; int op,x,y,k; while(m--) { scanf("%d%d%d%d",&op,&x,&y,&k); if(op==0) { update(x,y,k); } else if(op==1) { printf("%d\n",query(x,y,k)); } } } return 0; }
最後貼個模板:
分塊模板:
belong[]陣列表示第i個點屬於哪個塊,l[],r[]分別表示第i個點所在的左右邊界
block表示每塊的大小,num表示有多少塊
void build()
{
block=(int)sqrt(n);
num=n/block;
if(n%block) num++;
for(int i=1;i<=num;i++)
l[i]=(i-1)*block+1,r[i]=i*block;
r[num]=n;//最後一塊的邊界一定是n
for(int i=1;i<=n;i++){
cin>>a[i];
belong[i]=(i-1)/block+1;
}
//下面維護需要的陣列.....
}