洛谷 CF1552H. Guess the Perimeter
就你?这是 3300 的题??
记录了一些非正解想法和思考过程。
首先把格子转为点,最后再去掉四个角的周长。
很显然的做法是:可以枚举每一行,查询一整行的格子,答案只会是 $0$ 或者长度,枚举列同理,这样询问次数最坏是 $400$ 次。
考虑到这是一个网格,所以考虑对格子黑白染色的常规套路,但这显然并没有什么用。
考虑分治,每次把网格横着切成两半,如果上一半有矩形的一部分就往上递归分治,否则往下分治,但是这样的询问次数是两个 $\log$ 的。
但是显然我们可以先查询全局得到面积,这样只需要求长度,即可相除得到宽度,就变成了一个 $\log$ 外加一次查询。
分治并不好再往下优化了,但是黑白染色或许有一些出路?
把对格子的染色改成对行的染色,即每隔一行选一行的格子询问。
假设得到的答案为 $x$,那么没有被询问到格子数量为 $S-x$,只要满足 $x \not= S-x$,那么显然答案就是 $|(S-x)-x|$,即两部分之差。
但是 $x=S-x$ 怎么办?这样矩形的宽度就是偶数了,没法再确定。
宽度是偶数?把宽度除以二再查询就解决了。
也就是每隔 $2^i$ 行选择 $2^i$ 行查询,以 $2^i$ 行为一个单位进行黑白染色!
这样看似可行,但是问题在于,如果最后一个 $2^i$ 行切到了矩形的底部,你并不知道在矩形外的有多少行,也就无法计算。
上述做法不能成立的原因主要是 $2^i$ 为一个单位不知道切到矩形内部多少行,那么改成 $1$ 行就没有这个问题了!
考虑每 $2^i$ 内部选择第一行,即每选一行中间隔 $2^i-1$ 行再选,这样从小往大枚举 $i$ 直到出现类似于 $x \not= S-x$ 的情况。
把上面的情况扩展,如果有 $x \times 2^i = S$,那么就 $i + 1 \to i$。
否则 $x \not= S - x$,得到宽度为 $|\frac{S}{2^{i-1}}-2x|$。
这样虽然询问次数和分治一样还是一个 $\log$ 外加一次查询,但是 $2^i | S$ 是有单调性的!
因此可以二分 $i$,注意到这样的询问次数是 $\log \log 200 + 1 = 4$,刚好满足题目限制。
压行大师,哈哈,最喜欢这种思维层层递进代码又不难写的题了。
#include <bits/stdc++.h>
using namespace std;
int ans[210];
int query(int p) {
printf("? %d\n", (199 / p + 1) * 200);
for (int i = 1; i <= 200; i += p) for (int j = 1; j <= 200; j++) printf("%d %d ", i, j); puts("");
cout.flush(); scanf("%d", &ans[p]); return ans[p];
}
int main() {
int l = 1, r = 8, S = query(1);
while (l < r) {
int mid = l + r >> 1, x = query(1 << mid);
if ( (x << mid) ^ S) r = mid;
else l = mid + 1;
}
int a = abs( ( S >> (l - 1) ) - (ans[1 << l] << 1) ), b = S / a;
printf("! %d\n", (a + b) * 2 - 4);
return 0;
}

浙公网安备 33010602011771号