CF1764G3 Doremy's Perfect DS Class (Hard Version) 题解

Description

给定一个长度为 $ m $ 的数组 $ a $,Doremy 树支持查询 $ Q(l,r,k) $,其中 $ 1 \leq l \leq r \leq m $ 且 $ 1 \leq k \leq m $,该查询返回数组 $ \left[\left\lfloor\frac{a_l}{k} \right\rfloor, \left\lfloor\frac{a_{l+1}}{k} \right\rfloor, \ldots, \left\lfloor\frac{a_r}{k} \right\rfloor\right] $ 中不同整数的个数。

Doremy 有一个秘密排列 $ p $,它是 $ 1 $ 到 $ n $ 的一个排列。你可以进行查询,每次查询你给出 $ 3 $ 个整数 l,r,kl,r,k1lrn1 \leq l \leq r \leq n1kn1 \leq k \leq n),你会收到 Q(l,r,k)Q(l,r,k) 在数组 $ p $ 上的值。你能否在最多 $ \mathbf{20} $ 次查询内,找到下标 yy($ 1 \leq y \leq n $)使得 $ p_y=1 $?

注意,排列 $ p $ 在你进行任何查询之前就已经确定。

n1024n\leq 1024

Solution

首先询问肯定是考虑 kk 比较特殊的情况,比如 k=2k=2 或者 k=nk=n 这种很大或者很小的询问。

这里先询问 k=2k=2

对于 x/2\lfloor x/2\rfloor 相同的两个位置,所有包含它们中至少一个的区间会得到贡献 11,同时包含 11 的区间也会得到贡献 11。容易发现只有 11nn 是偶数时的 nn 会比较特殊,因为它们的颜色只有一个位置。

对于一个前缀 [1,i][1,i] 和后缀 [i+1,n][i+1,n]。设 c1=Q(1,i,2),c2=Q(i+1,n,2)c_1=Q(1,i,2),c_2=Q(i+1,n,2),则我们可以计算出横跨前后缀的颜色数量,然后就可以得出两边分别只有恰好一个位置的颜色数量 v1v_1v2v_2

考虑二分答案,如果 v1v2v_1\neq v_2,则往更大的那边走即可。如果 v1=v2v_1=v_2,则此时一定满足一边有 11,另一边有 nn,再通过询问 Q(1,mid,n)Q(1,mid,n) 即可得到 nn 在哪一边,11 则在另一边,往 11 那边继续递归即可。

经过计算会发现上面的做法次数是 2log2n+1=212\left\lceil\log_2n\right\rceil+1=21,会多一次。

这一次在二分区间长度为 22 的时候分讨一下即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <bits/stdc++.h>

// #define int int64_t

int n;

int query(int l, int r, int k) {
static std::map<std::tuple<int, int, int>, int> mp;
if (l > r) return 0;
if (l == r) return 1;
if (l == 1 && r == n) return n / k + 1;
if (mp.count({l, r, k})) return mp[{l, r, k}];
std::cout << "? " << l << ' ' << r << ' ' << k << '\n';
fflush(stdout);
int v;
std::cin >> v;
return mp[{l, r, k}] = v;
}

void dickdreamer() {
std::cin >> n;
int L = 1, R = n, posn = -1;
while (L < R) {
int mid = (L + R) >> 1;
// int c1 = query(1, mid, 2), c2 = query(mid + 1, n, 2);
int c1, c2;
if (L + 1 == R && n % 2 == 0) {
if (!~posn) {
c1 = query(1, L - 1, 2) + 1, c2 = query(R + 1, n, 2) + 1;
} else {
if (query(1, R, 2) == query(1, L - 1, 2) + 1) {
c1 = query(1, L, 2), c2 = query(R + 1, n, 2) + 1;
} else {
c1 = query(1, L - 1, 2) + 1, c2 = query(R, n, 2);
}
}
} else {
c1 = query(1, mid, 2), c2 = query(mid + 1, n, 2);
}
int cc = c1 + c2 - (n / 2 + 1);
// cc : 两边共有的数量
int v1 = 2 * c1 - mid - cc, v2 = 2 * c2 - (n - mid) - cc;
if (posn != -1) {
if (posn <= mid) --v1;
else --v2;
}
if (v1 > v2){
R = mid;
} else if (v1 < v2) {
L = mid + 1;
} else {
if (mid >= 2) {
if (query(1, mid, n) == 2) posn = mid, L = mid + 1;
else posn = mid + 1, R = mid;
} else {
if (query(mid + 1, n, n) == 2) posn = mid + 1, R = mid;
else posn = mid, L = mid + 1;
}
}
}
std::cout << "! " << L << '\n';
fflush(stdout);
}

int32_t main() {
// #ifdef ORZXKR
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// #endif
// std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF1764G3 Doremy&#39;s Perfect DS Class (Hard Version) 题解
https://sobaliuziao.github.io/2025/08/20/post/415ba1b2.html
作者
Egg_laying_master
发布于
2025年8月20日
许可协议