寫的簡單。主要是留給自己做複習資料。
「bzoj1901」dynamic rankings.
給定乙個含有 \(n\) 個數的序列 \(a_1,a_2 \dots a_n\),需要支援兩種操作:
引入整體二分。
其實就是我們對於二分到的乙個值域 mid,去離線針對所有的詢問進行 check。
具體是利用一些資料結構。
然後根據 check 和 mid 的大小將詢問分為兩撥,再分治下去解決問題。
參考於 oi-wiki 的板子很精簡且思路便於理解。
#include int abs (int x)
int max (int x, int y)
int min (int x, int y)
int read ()
while ('0' <= s && s <= '9')
x = (x << 3) + (x << 1) + (s ^ 48), s = getchar ();
return x * k;
}void write (int x)
void print (int x, char s)
const int maxn = 2e5 + 5;
char opt[3];
int ans[maxn], bit[maxn], a[maxn], n, m;
int low_bit (int x)
void update (int k, int x)
int query (int k)
struct node
node (bool t, int i, int x, int y, int k)
} q[maxn], tmp[2][maxn];
void solve (int l, int r, int l, int r)
int mid = (l + r) >> 1, cnt0 = 0, cnt1 = 0;
for (int i = l; i <= r; i++)
if (q[i].type)
else
for (int i = 1; i <= cnt0; i++)
if (!tmp[0][i].type)
update (tmp[0][i].x, -tmp[0][i].k);
for (int i = 1; i <= cnt0; i++)
q[l + i - 1] = tmp[0][i];
for (int i = 1; i <= cnt1; i++)
q[l + cnt0 + i - 1] = tmp[1][i];
solve (l, l + cnt0 - 1, l, mid), solve (l + cnt0, r, mid + 1, r);
}int main ()
else
q[++p] = node (0, 0, x, a[x], -1), q[++p] = node (0, 0, x, y, 1), a[x] = y;
}solve (1, p, 0, 1e9);
for (int i = 1; i <= tot; i++)
print (ans[i], '\n');
return 0;
}
「zjoi2013」k 大數查詢
需要維護 \(n\) 個可重整數集,集合的編號從 \(1\) 到 \(n\)。這些集合初始都是空集,有 \(m\) 個操作:
注意可重集的並是不去除重複元素的,如 \(\\cup\=\\)。
放在這裡是因為發現它比上道題的唯一差別在於區間修改。
也就是說整體二分的資料結構理論上可以百搭。比如線段樹。
#include typedef long long ll;
int abs (int x)
int max (int x, int y)
int min (int x, int y)
int read ()
while ('0' <= s && s <= '9')
x = (x << 3) + (x << 1) + (s ^ 48), s = getchar ();
return x * k;
}ll read_ll ()
while ('0' <= s && s <= '9')
x = (x << 3) + (x << 1) + (s ^ 48), s = getchar ();
return x * k;
}void write (int x)
void print (int x, char s)
const int maxn = 5e4 + 5;
int ans[maxn], n, m;
struct segment_tree
segment_node (int l, int r, ll s, ll la, bool c)
} tr[maxn * 4];
void push (int p)
if (tr[p].lazy)
}void pull (int p)
void make_tree (int p, int l, int r)
void update (int p, int l, int r, ll x)
push (p);
int mid = (tr[p].l + tr[p].r) >> 1;
if (l <= mid)
update (lson, l, r, x);
if (r > mid)
update (rson, l, r, x);
pull (p);
}ll query (int p, int l, int r)
#undef lson
#undef rson
} seg;
struct node
node (bool t, int i, int x, int y, ll k)
} q[maxn], tmp[2][maxn];
void solve (int l, int r, int l, int r)
int mid = (l + r) >> 1, cnt0 = 0, cnt1 = 0;
seg.tr[1].clear = true, seg.tr[1].sum = seg.tr[1].lazy = 0;
for (int i = l; i <= r; i++)
if (q[i].type)
else
for (int i = 1; i <= cnt0; i++)
q[l + i - 1] = tmp[0][i];
for (int i = 1; i <= cnt1; i++)
q[l + cnt0 + i - 1] = tmp[1][i];
solve (l, l + cnt0 - 1, l, mid), solve (l + cnt0, r, mid + 1, r);
}int main ()
solve (1, p, -n, n);
for (int i = 1; i <= tot; i++)
print (ans[i], '\n');
return 0;
}
整體二分專題
何謂整體二分?一般的二分只適用於單個詢問的,如果有很多個詢問,就變成了n 2n 2 n2或更高但整體二分則可以迅速處理多個詢問的問題 首先需要離線,讀入所有詢問 然後我們二分答案,這時候我們將詢問分成兩個部分,如果l r就直接更新答案,否則考慮分治,詢問的答案在左邊的丟到左邊,答案在右邊的則丟到右邊...
學習 整體二分
在?看看整體二分 整體二分是個啥,就是遞迴進行二分答案的操作,按照當前二分出的區間對詢問操作和修改操作進行左右分類。有點類似於歸併排序的樣子,但是需要用個維護區間的資料結構來維護當前詢問區間的區間的查詢和修改操作,每次查詢完當前區間的操作之後,需要清空之前的修改操作。整體二分可以保證會互相影響的操作...
整體二分總結
通常與 cdq 分治同類談論,處理的問題性質本質上有不同 整體二分,顯然整體 同時 處理多個二分查詢,通常帶有修改,我們需要分治處理 solve l,r,l,r 為操作 l,r 中答案均在 l,r 區間內 我們是分治處理 l,mid 操作的前 n 個為新增操作 靜態陣列 掃一遍操作,新增操作時把 v...