题解 P7109 【滴水不漏】
这题还是非常难想的,暴力的思路只能解决 Subtask 1和4。
总觉得情况很多复杂无比,对于多余的水一直很纠结该怎么放,有一个无穷大的容器可以接就好了。
但是想不出来并不影响我写题解
为了简化问题,先假设有一个无穷大的桶可以接水,也就是允许清空任意桶。为什么会想到清空呢?因为清空后的桶就沦落为工具人(桶)了。
首先 (地球人都知道吧),测完后把
执行? 2 1,如果第一个桶没满,说明 ? 1 2,这个返回值可以忽略,最后执行? 2 2 就能知道
这样测出 ? 3 1、? 3 2、? 3 3(除了最后一次,每次测完还需倒回去)。
这样一算的话,测到
其实没必要从小到大一个个枚举,换成二分就行了,这样算起来是不超过
现在的问题是,不存在一个无穷大的容器可以清空,这样只能把水往后倒了。
还是先从最最最简化的问题开始考虑:
假设每次清空都倒到第
n 个桶,并且前n-1 个桶清空完毕后,第n 个桶还是不满的。
这样,我们最后用二分测出最后一个桶的水量 ,其实是多了
如果情况稍稍复杂一点:
清空第
k 个桶的时候,第n 个桶突然满了
这时候,第
然后剩余的
设一个变量
现在思路就清晰了,在循环计算第
参考代码如下:
int ans[N];
int pour(int x, int y) {
int w;
printf("? %d %d\n", x, y);
fflush(stdout);
scanf("%d", &w);
return w;
}
int cal(int i) {
int l = 1, r = i + 1;
while (l < r) {
int mid = l + r >> 1;
if (pour(i, mid) == 0)
r = mid;
else
l = mid + 1;
pour(mid, i);
}
return l - 1;
}
int main() {
int n;
scanf("%d", &n);
int pos = n;
for (int i = 1; i <= pos; i++) {
int now = cal(i);
ans[i] += now;
while (i != pos) {
if (pour(i, pos) == 0) {
ans[pos] -= now;
break;
}
int tmp = now;
now = cal(i);
ans[pos] += pos - (tmp - now);
pos--;
}
}
printf("!");
for (int i = 1; i <= n; i++) printf(" %d", ans[i]);
puts("");
return 0;
}