洛谷 P6960 [NEERC2017] Interactive Sort

发布时间 2023-11-09 14:56:41作者: zltzlt

洛谷传送门

NOIP 模拟赛 T2。随机化交互好题。

\(a\) 为原题面中的 \(e\)\(b\) 为原题面中的 \(o\)

显然可以使用 \(\left\lceil\frac{n}{2}\right\rceil\) 次询问求出 \(a\) 中任意其中一个元素的值,全部问一遍 \(a_i\)\(b_j\) 的大小关系即可。但是这样很浪费。

考虑我们求出了一个 \(a_i\),我们还知道了所有 \(b_j\) 和这个 \(a_i\) 的大小关系。那么经过这 \(n\) 次询问后,\(b\) 被分成了两个值域的“块”,我们知道两个块的值域的 \([l, r]\) 和它们包含哪些位置。

那么当我们求每一个 \(a_i\)\(b\) 中的一个块会分成两个,除非 \(n\) 为偶数且 \(a_i = n\),这种情况特判即可。通过二分我们可以知道 \(a_i\) 可能在哪两个块中。二分 check 时和块内任意一个 \(b_j\) 比较即可。得到这两个块后,暴力把块中每个元素都比较一遍,就可以把其中一个块分成两个,同时也能知道 \(a_i\) 的值。

现在问题是询问次数没有保证。考虑以一个随机的顺序问 \(a_i\),问完 \(i\)\(a\) 后每个块的期望大小约为 \(\frac{n}{2i}\),暴力问两个块,最坏就是期望问 \(\frac{n}{i}\) 次。再加上二分的次数 \(\sum\limits_{i = 1}^n \log_2i\)\(n = 10000\) 时期望询问次数约为 \(1.45 \times 10^5\)。实际会略大,但是能过。

code
const int maxn = 10050;

int n, a[maxn], b[maxn], p[maxn];

struct node {
	int l, r;
	vector<int> ps;
};

void solve(int _n) {
	n = _n;
	node u;
	u.l = 1;
	u.r = (n + 1) / 2 * 2 - 1;
	for (int i = 1; i <= (n + 1) / 2; ++i) {
		u.ps.pb(i);
	}
	vector<node> vc;
	vc.pb(u);
	mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
	for (int i = 1; i <= n / 2; ++i) {
		p[i] = i;
	}
	shuffle(p + 1, p + n / 2 + 1, rnd);
	for (int i = 1; i <= n / 2; ++i) {
		int l = 0, r = (int)vc.size() - 1;
		while (r - l + 1 > 2) {
			int mid = (l + r) >> 1;
			if (ask(p[i], vc[mid].ps[0])) {
				l = mid;
			} else {
				r = mid;
			}
		}
		bool flag = 1;
		for (int j = l; j <= r; ++j) {
			vector<int> v1, v2;
			for (int x : vc[j].ps) {
				if (ask(p[i], x)) {
					v1.pb(x);
				} else {
					v2.pb(x);
				}
			}
			if (v1.empty() || v2.empty()) {
				continue;
			}
			flag = 0;
			if (j) {
				a[p[i]] = vc[j - 1].r + 1;
			}
			a[p[i]] += (int)v1.size() * 2;
			node u;
			u.l = a[p[i]] + 1;
			u.r = vc[j].r;
			vc[j].r = a[p[i]] - 1;
			vc[j].ps = v1;
			u.ps = v2;
			vc.insert(vc.begin() + j + 1, u);
			break;
		}
		if (flag) {
			a[p[i]] = n;
		}
	}
	for (node u : vc) {
		b[u.ps[0]] = u.l;
	}
	vector<int> v1, v2;
	for (int i = 1; i <= n / 2; ++i) {
		v1.pb(a[i]);
	}
	for (int i = 1; i <= (n + 1) / 2; ++i) {
		v2.pb(b[i]);
	}
	report(v1, v2);
}