整體二分是個很神的東西,它可以把許多複雜的資料結構題化簡。它的精髓在於巧妙地利用了離線的特點,把所有的修改、詢問操作整體把握。
先說說第k大數吧,這種問題是整體二分的標誌性題目,什麼劃分樹啊,主席樹啊,樹套樹啊見了整體二分都得自嘆不如。首先對於一次詢問來說我們可以二分答案,然後通過驗證比答案大的數有多少個來不斷地縮小答案範圍直至得到乙個準確的答案。而對於多個詢問我們同樣可以這麼做,只不過對每乙個詢問我們都需要判定一下,以決定它被劃分到哪乙個答案的區間裡。這個判定過程就是通過比較比二分的mid大的數的個數和k。同時我們看到,如果比二分的mid大的數的個數小於k了,我們是要去尋找小的答案,那麼這些比mid大的數在以後的遞迴裡始終會對答案有貢獻,所以我們沒必要去做重複的工作,只需要把這些數的個數累積到貢獻裡,以後遞迴的時候就不用考慮這些數了。具體地,我們把數列裡的數也和詢問一起遞迴,這樣這些數也會被分到屬於的答案區間裡,並且只對相應區間裡的詢問有影響。如果有修改呢,我們把修改看成乙個數就好了,一樣可以隨著遞迴不斷地被劃分下去。
我們看到,整體二分的過程實質上是個按照數值來劃分操作序列的過程,於是我們的複雜度也就和操作序列的長度線性相關,那麼我們在中間維護一些資訊的時候,就一定不能有何數列長線性相關的東西,否則會破壞其時間複雜度,具體的複雜度證明請見2023年集訓隊xhr**。
貼個典型性的**吧:
帶修改區間第k小數
view code
1 #include2 #include3 #include4 #include5 #include6樹狀陣列什麼的我最喜歡了,又短又快!#define maxn 220000
7#define inf 100000000089
using
namespace
std;
10struct
query
11q[maxn],q1[maxn],q2[maxn];
14int
a[maxn],ans[maxn],tmp[maxn],t[maxn];
15int
n,m,num,cnt;
1617
void add(int x,int
y)18
2122
int ask(int
x)23
2829
void divide(int head,int tail,int l,int
r)30
39int mid=(l+r)>>1;40
for (int i=head;i<=tail;i++)
4148
for (int i=head;i<=tail;i++)
4954
int l1=0,l2=0;55
for (int i=head;i<=tail;i++)
56if (q[i].tp==3)57
65}66else
6771
for (int i=1;i<=l1;i++) q[head+i-1]=q1[i];
72for (int i=1;i<=l2;i++) q[head+l1+i-1]=q2[i];
73 divide(head,head+l1-1
,l,mid);
74 divide(head+l1,tail,mid+1
,r);
75}
7677
intmain()
7888
char
sign;
89int
x,y,z;
90for (int i=1;i<=m;i++)
9199
else
100108
}109 divide(1,num,0
,inf);
110for (int i=1;i<=cnt;i++)
111 printf("
%d\n
",ans[i]);
112return0;
113 }
**裡有許多可以縮起來寫的地方,我沒改,目測這樣也比主席樹短不少......
整體二分專題
何謂整體二分?一般的二分只適用於單個詢問的,如果有很多個詢問,就變成了n 2n 2 n2或更高但整體二分則可以迅速處理多個詢問的問題 首先需要離線,讀入所有詢問 然後我們二分答案,這時候我們將詢問分成兩個部分,如果l r就直接更新答案,否則考慮分治,詢問的答案在左邊的丟到左邊,答案在右邊的則丟到右邊...
學習 整體二分
在?看看整體二分 整體二分是個啥,就是遞迴進行二分答案的操作,按照當前二分出的區間對詢問操作和修改操作進行左右分類。有點類似於歸併排序的樣子,但是需要用個維護區間的資料結構來維護當前詢問區間的區間的查詢和修改操作,每次查詢完當前區間的操作之後,需要清空之前的修改操作。整體二分可以保證會互相影響的操作...
整體二分總結
通常與 cdq 分治同類談論,處理的問題性質本質上有不同 整體二分,顯然整體 同時 處理多個二分查詢,通常帶有修改,我們需要分治處理 solve l,r,l,r 為操作 l,r 中答案均在 l,r 區間內 我們是分治處理 l,mid 操作的前 n 個為新增操作 靜態陣列 掃一遍操作,新增操作時把 v...