數列分塊入門 1
題目鏈接https://loj.ac/problem/6277
內存限制:256 MiB時間限制:100 ms
題目描述
給出一個長爲 n 的數列,以及 n個操作,操作涉及區間加法,單點查值。
輸入格式
第一行輸入一個數字 。
第二行輸入 n 個數字,第 i 個數字爲 ai,以空格隔開。
接下來輸入n 行詢問,每行輸入四個數字 opt,l,r,c以空格隔開。
若pot =0 ,表示將位於[l,r] 的之間的數字都加 。
若opt =1 ,表示詢問 ar 的值(l 和 c 忽略)。
輸出格式
對於每次詢問,輸出一行一個數字表示答案。
樣例
樣例輸入
4
1 2 2 3
0 1 3 1
1 0 1 0
0 1 2 2
1 0 2 0
樣例輸出
2
5
數據範圍與提示
對於 100% 的數據1<=n<=50000,-2^31<=others,ans<=2^31 -1.
。。。很顯然這一題我們可以用樹狀數組或者線段樹來做,但既然要練習分塊。。。
分塊實際上就是暴力枚舉,只不過他是優化了的暴力。我們先將1----n分爲sqrt(n)個大小爲sqrt(n)的塊,當然,對於不能完全平方的,我們需要將塊數+1。還需要做的就是將每個塊的左右端點記錄一下:
int 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;
然後我們將1---n的所有數打上標記(即所屬的塊是哪一個)。接下來進行區間操作的時候我們可以利用線段樹lazy標記的思想,將l和r所屬的塊單獨更新一下,id(l)+1到id(r)-1的塊我們記錄一下這個塊更新了就好了:
int p=id[l],q=id[r];
for (int i=p+1; i<=q-1; i++) add[i]+=c;
for (int i=l ; i<=R[p]; i++) a[i]+=c;
for (int i=L[q]; i<=r; i++) a[i]+=c;
那麼最後的答案就是a[r]+add[id[r]],即它本身的值加上他所屬的塊所加上的值
以下是AC代碼:
#include <bits/stdc++.h>
using namespace std;
const int mac=5e4+10;
inline void in(int &read)
{
int x=0;
char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
read=x;
}
inline void out(int x)
{
if (x>=10){
out(x/10);
}
putchar(x%10+'0');
}
int a[mac],L[mac],R[mac],id[mac],add[mac];
void update(int l,int r,int c)
{
int p=id[l],q=id[r];
if (p==q)
for (int i=l; i<=r; i++) a[i]+=c;
else {
for (int i=p+1; i<=q-1; i++) add[i]+=c;
for (int i=l ; i<=R[p]; i++) a[i]+=c;
for (int i=L[q]; i<=r; i++) a[i]+=c;
}
}
int main()
{
int n;
in(n);
for (int i=1; i<=n; i++)
in(a[i]);
int 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++)
id[j]=i;
for (int i=1; i<=n; i++){
int opt,l,r,c;
in(opt); in(l); in(r); in(c);
if (!opt) {
update(l,r,c);
}
else {
out(a[r]+add[id[r]]);
putchar('\n');
}
}
return 0;
}