KLC 數點學習筆記

IANYEYZ發表於2024-07-31

KLC 數點由 KLC 大神在模擬賽中發明。

其演算法複雜度與答案值域大小掛鉤。

其能解決的問題一般有著如下的特點:給定一個序列,每次詢問一個區間有多少個子區間滿足什麼性質,資料隨機生成。

其演算法流程為:

  1. 透過某種方法預處理出所有滿足性質的子區間

  2. 將得到的區間表示在二維平面上

  3. 將詢問離線,轉化為二維數點,使用掃描線解決

其答案依賴於期望分析。

例題:

給定一個長度為 \(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";
	}
}

相關文章