ZROJ3265 猜数游戏
显示器上有五个数位,每个位置可以显示 \(0\) 到 \(9\),也就是说显示器可以显示 \(0\) 到 \(99999\) 的所有数。
初始所有位置都是 \(0\)。给定 \(L,R\),系统将从 \([L,R]\) 中随机选出一个数字 \(x\),我们的目标是猜出这个数。
每一秒,我们可以干两件事情中的一件:
- 将显示器上的某一个数位上的数字修改为任意数字。
- 询问当前数字和 \(x\) 的大小关系(返回大于,等于或小于)。
你需要求出,在最劣情况下,我们最少需要几秒可以确定出一个唯一的 \(x\)。
\(1 \le n \le 10^5\),最多 \(50\) 次多测,\(8\) 秒。
一开始一直想着直接构造,结果直接炸了。()
我们考虑 \(O(n^3)\) 的区间 dp。
我们发现每一次的转移点要么在当前区间的左边,否则右边。
那么我们设 \(f(l,r,0/1)\),表示确定答案在 \([l,r]\) 范围内,当前显示屏上的数字在 \(l-1/r+1\) 的位置。
不难发现转移封闭,那么我们就成功拿到了 \(40\) 的暴力分。
考虑优化,我们发现这道题答案值域极其小(\(\le 5\log n\)),打表甚至可以发现答案 \(\le 42\)。
因此考虑把这个作为 dp 对象。我们设 \(g(x,k,0/1)\),表示 \(x\) 作为 左/右 端点时最靠 右/左 的答案 \(\le k\) 的另外一个端点。
考虑转移,以 \(g({x,k,0})\) 为例。
相当于找到一个点 \(p\) 作为下一个切割的点,满足 \(g({p-1,k-1-d(x,p),1})\le x\),贡献就是 \(g({p+1,k-1-d(x,p),0})\)。
这里 \(d(x,y)\) 表示在显示器上把 \(x\) 改为 \(y\) 的最小操作次数。
做到这一点后我们从小到大枚举 \(k\),那么就可以在 \(\mathcal O(n^2\log n)\) 解决。
考虑继续优化,我们发现阻挡我们进一步优化的瓶颈在于 \(d\) 函数无法优美地表示。
加入一个巧妙的转化,我们发现这个相当于我们可以选择将 \(x,y\) 的每一位改成 \(-1\),然后最小的能够使得 \(x\) 与 \(y\) 完全相同的修改次数就是 \(d(x,y)\) 的两倍。
这个形式特别像高位前缀和,考虑将原本的数看作 1, \(-1\) 看作 0,那么贡献与求和的形式其实就是高位前缀与后缀和。
这样我们就可以以 \(2^5\) 的单次代价暴力往上加 \(d\) 的贡献,保存到一个 \(11^5\) 的桶中,\(10^5\) 这个数可能需要单独处理。
那么接下来我们扫描线,在每个位置上维护可以转化到他的数(高位后缀和),查询的时候暴力遍历。
这一部分可以在 \(42*32*n=1344n\) 的时间内解决。
最后是查询,由于第一次分割比较特殊,我们考虑枚举第一次的切割点,然后二分答案。
那么可以简单地在 \(O(Tn\log\log n)\) 的时间内完成。
至此就解决了这道问题。
后记:
如果答案值域小,可以考虑把答案作为 dp 限制,原限制作为 dp 对象。
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/19025778

浙公网安备 33010602011771号