题解:P16434 [APIO 2026 中国赛区] 蛋糕

· · 题解

场外被家长做局人士竟然会了黑题,然后发现降紫了。尼玛。

题意

交互库有一个正整数 d\in [1,W];给你三个常数 N,W,K,你可以构造一个长度为 m\le N 的值域为 [1,W+200] 的序列 a 给交互库,交互库会将 d 插入 a 并排序,成为新序列 b。你每次可以询问两个下标集合 S_1,S_2,交互库会告诉你 \sum\limits_{i\in S_1}b_i\sum\limits_{j\in S_2}b_j 的大小关系(<>=),要求在至多 K 次询问后求出 d

思路 Part 1

Case 1: N=3000,W=100,K=100

我说这是我最后会的 Case 有没有懂的。

你发现你可以塞的数很多,所以你考虑填满这个值域 [1,100]。由于 d\in[1,100] 所以会有恰好一组相邻两个数相等,其余的相邻两个数都差 1

于是我们可以列出判断方法 1+a_i?a_{i+1},在 a 开头塞一个 1。枚举 i199,若 b_0+b_i=b_{i+1} 说明 d=i

思路 Part 2

Case 2: N=3,W=3,K=1

我说这个对我 Case 4 毫无提示性有没有懂的。

注意到一次询问至多传递三种信息,我们需要让这三种信息分别对应三种数。

这很简单,我们构造 a=\{100,102\},那么 d 一定成为了 b_0。询问一次 ([0,1],[2]),返回的 <,=> 分别对应 1,23

思路 Part 3

Case 3: N=40,W=10^9,K=30

灵机一动!

注意到 30 恰好等于 \lceil \log_2 W\rceil。那我们的构造肯定和 2 的次幂有关。

那么我们构造 a=\{1,2^0,2^1,2^2,\cdots,2^{29}\}。这个序列有什么性质呢?就是每一项都等于其前一项的前缀和。

对于一个位置 p\in[1,30] 询问 ([0,1,\cdots,p-1],[p])。若结果为 = 说明 d 不在这个前缀里。那么我们从后往前扫,找到第一个结果为

接下来我们得到了 $2^{p-1}\le d<2^p$,于是有 $\lceil\log_2 (d-2^{p-1})\rceil=p-1$。很容易想到使用倍增对 $d-2^{p-1}$ 进行二进制拆分,花费次数为 $p-1$。 两个加起来刚好 $30$ 次。 ### 思路 Part 4 Case 4: $N=3000,W=2000,K=7$。 我说上厕所超级有用有没有懂的。 $K=7$ 是个啥阴。 $\log_2$ 爆炸了你是不是只能考虑 $\log 3$ 了。然后我们发现 $2200$ 恰好塞得下 $[1,3^7]$。 于是我们构造 $a=\{1,2,\cdots,2186,2187\}$。塞了一个 $d$ 后,我们可以看成 $b$ 原本是一个大小为 $3^7+1$ 的排列,因为宇宙射线的影响,对一个后缀 $[d,2188]$ 恰好全部减了 $1$! 假设某一层我们递归到了一个区间 $[l,r]$,我们选取四个位点 $p_0,p_1,p_2,p_3$ 分别为 $l,\frac{2\times l+r}{3},\frac{l+2\times r}{3},r$,然后询问 $([p_0,p_3],[p_1,p_2])$。注意到: - $d\in [p_0,p_1]$ 时,受宇宙射线影响的位置有 $p_1,p_2,p_3$,询问的结果为 $>$; - $d\in [p_1,p_2]$ 时,受宇宙射线影响的位置有 $p_2,p_3$,询问的结果为 $=$; - $d\in [p_2,p_3]$ 时,受宇宙射线影响的位置有 $p_3$,询问的结果为 $<$。 一个结果恰好对应一个子区间!这样我们就能保证每次的 $r-l$ 恰好除以 $3$。到了 $r-l=1$ 时返回 $d=r$ 即可。 ### 代码 ```cpp #include <bits/stdc++.h> using namespace std; typedef int ll; int compare_tastiness(std::vector<int> S1, std::vector<int> S2); std::vector<int> bake_cakes(int N, int W, int K) { vector<ll> res; if (W == 100 && K == 100) { res.push_back(1); for (ll i = 1; i <= 100; i ++ ) res.push_back(i); } else if (W == 3 && K == 1) { res.push_back(100); res.push_back(102); } else if (W == 1000000000 && K == 30) { res.push_back(1); for (ll i = 0; i < 30; i ++ ) res.push_back(1 << i); } else if (W == 2000 && K == 7) { for (ll i = 1; i <= 2187; i ++ ) res.push_back(i); } return res; } int find_tastiness(int m, int W, int K) { vector<ll> S1, S2; if (W == 100 && K == 100) { ll p = 1; for ( ; p < 100; p ++ ) { S1.clear(), S2.clear(); S1.push_back(0), S1.push_back(p), S2.push_back(p + 1); if (compare_tastiness(S1, S2)) return p; } return 100; } else if (W == 3 && K == 1) { S1.push_back(0), S1.push_back(1), S2.push_back(2); ll t = compare_tastiness(S1, S2); return (t == -1 ? 1 : (t == 0 ? 2 : 3)); } else if (W == 1000000000 && K == 30) { ll t = 30; for ( ; t >= 1; t -- ) { S1.clear(), S2.clear(); for (ll i = 0; i < t; i ++ ) S1.push_back(i); S2.push_back(t); if (compare_tastiness(S1, S2) == 0) break; } ll res = 1 << (t - 1); S1.clear(), S2.clear(); S1.push_back(t); S2.push_back(t + 1); for (ll i = t - 2; i >= 0; i -- ) { S1.push_back(i + 1); if (compare_tastiness(S1, S2) == 1) S1.pop_back(); else res += 1 << i; } return res; } else if (W == 2000 && K == 7) { ll l = 0, r = 2187, t, p1, p2; while (r > l + 1) { p1 = (l + l + r) / 3, p2 = (l + r + r) / 3; S1.clear(), S2.clear(); S1.push_back(l), S1.push_back(r), S2.push_back(p1), S2.push_back(p2); t = compare_tastiness(S1, S2); if (t == 1) r = p1; if (t == -1) l = p2; if (t == 0) l = p1, r = p2; } return r; } } ```