整体二分

学了一下,感觉海星。

说是整体二分,不如就说是分治。

考虑这个问题:Luogu P3834 【模板】可持久化线段树 2

\(m\) 次询问,每次询问给出 \((l,r,k)\),问 \([l,r]\) 中第 \(k\) 小的值。

一个显然的结论:答案满足单调性,可以二分。

我们首先先简化一下:如果 \(m=1\),怎么维护?

先二分出一个 \(x\),建立树状数组维护 \(\le x\) 的个数,判断其是否小于 \(k\) 即可。

那加入 \(m\) 次询问怎么办呢?

可以离线下来,运用分治思想将同一范围内的答案统一处理。

具体的,我们定义结构体 \((x,y,k,id,opt)\)。对于询问,\(x,y,k\) 表示查询 \([x,y]\) 的第 \(k\) 小,此时的 \(opt=1\),对于原数,\(x\) 表示原值。

定义操作函数 solve(l,r,L,R) 表示当前处理到 \([l,r]\),值域为 \([L,R]\),对于 \(L=R\),显然,\(l\sim r\) 的答案就为 \(L\)\(R\))。

  • 对于原数,如果 \(x\le mid\),那么其在树状数组上的位置就加一。

  • 对于询问,查询 \([x,y]\) 的个数相当于查询原数的值在 \([L,mid]\) 的个数,我们令为 \(p\),如果 \(p\ge k\),那么此时答案在 \([L,mid]\) 中,否则在 \([mid+1,R]\) 中,需要注意,此时的 \(q_i\)\(k\) 要减去 \(p\)

码风有点抽象,切勿锐评 /qd。

当然你想要正常一点的也可以私信我。

点击查看代码
#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 2e5 + 100;

int n, m, cnt, mn = 1e18, mx = -1e18, ans[N];

struct Node {
	int x, y, k, id, opt;
} q[N], q1[N], q2[N];

struct Bit {
	int f[N];
	int lowbit(int x) { return x & -x; }
	void add(int x, int k) { while (x <= n) { f[x] += k; x += lowbit(x); } }
	int getsum(int x) { int sum = 0; while (x) { sum += f[x]; x -= lowbit(x); } return sum; }
} bit;

void solve(int l, int r, int L, int R) {
	if (l > r) return ;
	if (L == R) { for (int i = l; i <= r; i++) { if (q[i].opt) { ans[q[i].id] = L; } } return ; }
	int mid = (L + R) >> 1, p1 = 0, p2 = 0;
	for (int i = l; i <= r; i++) { if (!q[i].opt) { if (q[i].x <= mid) { bit.add(q[i].id, 1); q1[++p1] = q[i]; } else { q2[++p2] = q[i]; } } else { int num = bit.getsum(q[i].y) - bit.getsum(q[i].x - 1); if (num >= q[i].k) { q1[++p1] = q[i]; } else { q[i].k -= num; q2[++p2] = q[i]; } } }
	for (int i = 1; i <= p1; i++) { if (!q1[i].opt) { bit.add(q1[i].id, -1); } } 
	for (int i = 1; i <= p1; i++) { q[i + l - 1] = q1[i]; }
	for (int i = 1; i <= p2; i++) { q[i + l + p1 - 1] = q2[i]; }
	solve(l, l + p1 - 1, L, mid), solve(l + p1, r, mid + 1, R);
}

signed main() {
	cin >> n >> m;
	for (int i = 1, x; i <= n; i++) { cin >> x; q[++cnt] = {x, 0, 0, i, 0}; mn = min(mn, x), mx = max(mx, x); }
	for (int i = 1, l, r, k; i <= m; i++) { cin >> l >> r >> k; q[++cnt] = {l, r, k, i, 1}; }
	solve(1, cnt, mn, mx);
	for (int i = 1; i <= m; i++) cout << ans[i] << '\n';
	return 0;
}

Luogu P2617 Dynamic Rankings

纪念调了一个小时。

和上一题一样,但是要离散化。对于修改记录一下在树状数组上修改相应的值即可。

但是,要注意,这题按上一题的写法不行,因为上一题我的代码中原数记录的是 \(q_i\)\(x\),而询问记录的是 \(q_i\)\(k\),而这题要离散化,所以要统一成为 \(k\)

因为这个调了一个小时。

这题的代码和上题差不多。所以你可以提取上一题正常码风的代码。

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 100;

int n, m, cnt, ans[N], a[N], b[N], tot, in[N];

struct Node {
	int opt, k, x, y, id;
} q[N], q1[N], q2[N];

struct Bit {
	int f[N];
	int lowbit(int x) { 
		return x & -x; 
	}
	void add(int x, int k) { 
		while (x <= n) { 
			f[x] += k; 
			x += lowbit(x); 
		} 
	}
	int getsum(int x) { 
		int sum = 0; 
		while (x) { 
			sum += f[x]; 
			x -= lowbit(x); 
		} 
		return sum; 
	}
} bit;

void solve(int l, int r, int L, int R) {
	if (l > r) return ;
	if (L == R) { 
		for (int i = l; i <= r; i++) { 
			if (!q[i].opt) { 
				ans[q[i].id] = L; 
			} 
		} 
		return ;
	}
	int mid = (L + R) >> 1, p1 = 0, p2 = 0;
	for (int i = l; i <= r; i++) { 
		if (q[i].opt) { 
			if (q[i].k <= mid) { 
				bit.add(q[i].id, q[i].opt); 
				q1[++p1] = q[i]; 
			} else { 
				q2[++p2] = q[i]; 
			} 
		} else { 
			int num = bit.getsum(q[i].y) - bit.getsum(q[i].x - 1); 
			if (num >= q[i].k) { 
				q1[++p1] = q[i]; 
			} else { 
				q[i].k -= num; 
				q2[++p2] = q[i]; 
			} 
		} 
	}
	for (int i = 1; i <= p1; i++) { 
		if (q1[i].opt) { 
			bit.add(q1[i].id, -q1[i].opt); 
		} 
	} 
	for (int i = 1; i <= p1; i++) { 
		q[i + l - 1] = q1[i]; 
	}
	for (int i = 1; i <= p2; i++) { 
		q[i + l + p1 - 1] = q2[i]; 
	}
	solve(l, l + p1 - 1, L, mid);
	solve(l + p1, r, mid + 1, R);
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) { 
		cin >> a[i]; 
		b[++tot] = a[i];
		q[++cnt] = {1, a[i], 0, 0, i}; 
	}
	for (int i = 1; i <= m; i++) {
		char c;
		cin >> c;
		if (c == 'Q') {
			int l, r, k;
			cin >> l >> r >> k;
			q[++cnt] = {0, k, l, r, i};
		} else {
			int l, r;
			cin >> l >> r;
			q[++cnt] = {-1, a[l], 0, 0, l};
			q[++cnt] = {1, r, 0, 0, l};
			b[++tot] = r;
			a[l] = r;
		}
	}
	sort(b + 1, b + tot + 1);
	tot = unique(b + 1, b + tot + 1) - b - 1;
	for (int i = 1; i <= cnt; i++) {
		if (!q[i].opt) {
			continue;
		}
		int h = lower_bound(b + 1, b + tot + 1, q[i].k) - b;
		in[h] = q[i].k;
		q[i].k = h;
	}
	solve(1, cnt, 1, tot);
	for (int i = 1; i <= m; i++) {
//		cout << ans[i] << ' ';
		if (ans[i]) {
			cout << in[ans[i]] << '\n';
		}
	}
	return 0;
}

Luogu P3527 [POI2011] MET-Meteors

这道题不需要离散化,但是要注意区间总长度为 \(2\times m\),然后树状数组维护即可。

点击查看代码
#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 8e6 + 100;

int n, m, cnt, ans[N], t;

struct Node {
	int x, y, k, id;
} q[N];

struct Edge {
	int val, id;
} g[N], q1[N], q2[N];

vector<int> G[N];

struct Bit {
	int f[N];
	int lowbit(int x) { 
		return x & -x; 
	}
	void add(int x, int k) { 
		while (x <= 2 * m) { 
			f[x] += k; 
			x += lowbit(x); 
		} 
	}
	int getsum(int x) { 
		int sum = 0; 
		while (x) { 
			sum += f[x]; 
			x -= lowbit(x); 
		} 
		return sum; 
	}
} bit;

int query(int x) {
	int ans = 0;
	for (int i = 0; i < G[g[x].id].size(); i++) {
		ans += bit.getsum(G[g[x].id][i]) + bit.getsum(G[g[x].id][i] + m);
		if (ans >= g[x].val) {
			return ans;
		}
	}
	return ans;
}

void solve(int l, int r, int L, int R) {
	if (l > r) {
		return ;
	}
	if (L == R) { 
		for (int i = l; i <= r; i++) {
			ans[g[i].id] = L;
		}
		return ; 
	}
	int mid = (L + R) >> 1, p1 = 0, p2 = 0;
	for (int i = L; i <= mid; i++) {
		bit.add(q[i].x, q[i].k);
		bit.add(q[i].y + 1, -q[i].k);
	}
	for (int i = l; i <= r; i++) {
		int num = query(i);
		if (num >= g[i].val) {
			q1[++p1] = g[i];
		} else {
			g[i].val -= num;
			q2[++p2] = g[i];
		}
	}
	for (int i = L; i <= mid; i++) { 
		bit.add(q[i].x, -q[i].k);
		bit.add(q[i].y + 1, q[i].k);
	} 
	for (int i = 1; i <= p1; i++) { 
		g[i + l - 1] = q1[i]; 
	}
	for (int i = 1; i <= p2; i++) { 
		g[i + l + p1 - 1] = q2[i]; 
	}
	solve(l, l + p1 - 1, L, mid);
	solve(l + p1, r, mid + 1, R);
}

signed main() {
	cin >> n >> m;
	for (int i = 1, x; i <= m; i++) {
		cin >> x;
		G[x].push_back(i);
	}
	for (int i = 1, x; i <= n; i++) {
		cin >> x;
		g[i] = {x, i};
	}
	cin >> t;
	for (int i = 1, l, r, k; i <= t; i++) {
		cin >> l >> r >> k;
		q[i] = {l, r, k, i};
		if (r < l) {
			q[i].y += m;
		}
	}
	t++;
	q[t] = {1, 2 * m, (int)1e9, t};
	solve(1, n, 1, t);
	for (int i = 1; i <= n; i++) {
//		cout << ans[i] << ' ';
		if (ans[i] == t) {
			cout << "NIE\n";
		} else {
			cout << ans[i] << '\n';
		}
	}
	return 0;
}
posted @ 2024-08-06 21:35  ydq1101  阅读(22)  评论(0)    收藏  举报