KLC 數點由 KLC 大神在模擬賽中發明。
其演算法複雜度與答案值域大小掛鉤。
其能解決的問題一般有著如下的特點:給定一個序列,每次詢問一個區間有多少個子區間滿足什麼性質,資料隨機生成。
其演算法流程為:
-
透過某種方法預處理出所有滿足性質的子區間
-
將得到的區間表示在二維平面上
-
將詢問離線,轉化為二維數點,使用掃描線解決
其答案依賴於期望分析。
例題:
給定一個長度為 \(n\) 的數列,每次詢問區間 \([l, r]\) 中子區間是值域連續段的個數, \(q\) 次詢問。資料隨機生成(注:原題沒有該性質,但是仍可過,為保證演算法正確性,在這裡放了修改版)
考慮 KLC 數點,期望分析容易得到長度為 \(2\) 的值域連續段約有 \(O (1)\) 個,長度增加時出現次數更少,長度為 \(1\) 的值域連續段顯然有 \(n\) 個,那麼總共約有 \(O (n)\) 個,總複雜度為 \(O ( log n)\).
程式碼:
#include <bits/stdc++.h>
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, l, r) for (int i = l; i >= r; i--)
using namespace std;
int f[100010][30], g[100010][30], a[100010], ans[100010], n, q, l, r;
vector<array<int, 4> > v[100010];
struct BIT {
int tree[100010];
int lowbit(int x) { return x & (-x); }
void modify(int u, int d) {
while (u <= n) {
tree[u] += d;
u += lowbit(u);
}
}
int query(int u) {
int res = 0;
while (u) {
res += tree[u];
u -= lowbit(u);
}
return res;
}
}bit;
int findMin(int l, int r) {
int s = log2(r - l + 1);
return min(f[l][s], f[r - (1 << s) + 1][s]);
}
int findMax(int l, int r) {
int s = log2(r - l + 1);
return max(g[l][s], g[r - (1 << s) + 1][s]);
}
signed main() {
// freopen("cat.in", "r", stdin);
// freopen("cat.out", "w", stdout);
cin >> n;
rep (i, 1, n) {
cin >> a[i];
f[i][0] = a[i];
g[i][0] = a[i];
}
rep (j, 1, log2(n)) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
g[i][j] = max(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]);
}
}
rep (i, 1, n) {
// v[i].push_back({0, i, 1, 0});
rep (j, i + 1, n) {
int mmin = findMin(i, j), mmax = findMax(i, j);
if (mmax - mmin + 1 == (j - i + 1)) {
v[i].push_back({0, j, 1, 0});
} else {
j = mmax - mmin + i - 1;
}
}
}
cin >> q;
rep (i, 1, q) {
cin >> l >> r;
v[l - 1].push_back({1, l, r, -i});
v[r].push_back({1, l, r, i});
ans[i] = r - l + 1;
}
rep (i, 1, n) {
for (auto j:v[i]) {
if (j[0]) {
int id = 0, val = 0;
if (j[3] > 0) id = j[3], val = 1;
else id = -j[3], val = -1;
// cout << id << " " << bit.query(j[2]) - bit.query(j[1] - 1) << "\n";
ans[id] += (bit.query(j[2]) - bit.query(j[1] - 1)) * val;
} else {
bit.modify(j[1], j[2]);
}
}
}
rep (i, 1, q) {
cout << ans[i] << "\n";
}
}