1. 程式人生 > >分塊(簡單版樹狀數組,線段樹)

分塊(簡單版樹狀數組,線段樹)

cli i++ lose 預處理 查詢 lan \n strong 格式

前言

首先,在NOIP的比賽裏分塊是一個很好的水分神器,因為它可以代替樹狀數組,線段樹,但是如果出題人要卡你的程序的話......

分塊思想

包含n個元素的整數數組A,每次可以 C(i, j) : 修改一個元素A[i] = j Q(i, j) : 詢問A[i]+A[i+1]+…+A[j]的值

如何設計算法,使得修改和詢問操作的時間復雜度盡量低? 顯然存在修改O(1),查詢O(n)的算法。

每次重新計算,一些未被修改區間也經常被重復求和,於是我們可以將A[1..n]均分成sqrt(n)塊,每塊內部的元素為sqrt(n)個。

修改操作時只需要修改A[i]及A[i]所在的塊。

詢問操作時是需要枚舉i和j所在的塊內部,及其之間的塊。

時間復雜度在O(sqrt(n))級別。

主要思想就是將待操作的長度為N的區間分成大小為sqrt(N)的塊,然後實現各種操作……

一些常用定義:

  MAGIC:定義一個塊的大小,如字面意思,一個莫名其妙的數字……

  於是,我們把一段長度為N的區間,分成了若幹長度為 MAGIC 的區間:[0,magic),[magic, 2magic)....

   於是易得,i / MAGIC 就是點 i 所在塊的編號,若 i % MAGIC == 0,則證明由點 i 開始是一個新區間

一般來講,我們在預處理和修改的時候,維護兩個信息,一個是序列,另一個是塊

例題

講了這麽多還是來看例題吧......

鏈接點我!!!

#6277. 數列分塊入門 1

題目描述

給出一個長為 n的數列,以及 n 個操作,操作涉及區間加法,單點查值。

輸入格式

第一行輸入一個數字 n

第二行輸入 n 個數字,第 iii 個數字為 ai??,以空格隔開。

接下來輸入 n 行詢問,每行輸入四個數字 optlr、c,以空格隔開。

opt=0,表示將位於 [l,r] 的之間的數字都加 c

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,−231≤othersans≤231−1

題解:

  (建議大家邊看程序邊題解)

  這是一題區間修改,單點查詢可以用樹狀數組,線段樹(這不是放*嗎

  但是我們在這裏考慮分塊;

  首先,我們開一個最普通的數組來記錄每個元素的權值;

  然後再開一個數組來記錄每個元素所對應的分塊;

  然後再再(語文有點不好......)開一個數組來記錄每個分塊要加的值;

  每次尋找 l , r ,之間的區間,在分塊內部的直接打上標記;

  如果在分塊外面的直接暴力修改;

  嗯,代碼......

技術分享圖片
 1 //NOIPRP++
 2 #include<bits/stdc++.h>
 3 #define Re register int
 4 using namespace std;
 5 int N,a[50005],f[50005],opt,l,r,c,Sqr,Tage[50005]; 
 6 inline void read(int &x){
 7     x=0; char c=getchar(); bool p=1;
 8     for (;0>c||c>9;c=getchar()) if (c==-) p=0;
 9     for (;0<=c&&c<=9;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
10     p?:x=-x;
11 }
12 inline void Add(int l,int r,int c){
13     for (Re i=l;i<=min(f[l]*Sqr,r);i++) a[i]+=c;
14     if (f[l]^f[r]) for (Re i=(f[r]-1)*Sqr+1;i<=r;i++) a[i]+=c;
15     for (Re i=f[l]+1;i<=f[r]-1;i++) Tage[i]+=c;
16 }
17 int main(){
18     Re i,j;
19     read(N);Sqr=sqrt(N);
20     for (i=1;i<=N;i++) f[i]=(i-1)/Sqr+1;
21     for (i=1;i<=N;i++) read(a[i]);
22     for (i=1;i<=N;i++){
23         read(opt);read(l);read(r);read(c);
24         if (opt==0) Add(l,r,c);
25         if (opt==1) printf("%d\n",a[r]+Tage[f[r]]);
26     }
27     return 0;
28 }
29 //NOIPRP++
View Code

分塊(簡單版樹狀數組,線段樹)