矿场设备问题 题解
这道题是基于验证答案的。直接对设备数量求解并不简单,但是通过对一个答案是否正确进行验证,就可以解决这道题。
我们首先假设答案是 \(x\),考虑在什么情况下这个答案合法。
我们考虑一个策略,就是首先向前走 \(x\) 步,并且途径设备全部打开。最后我们走到第 \(x+1\) 个节点,再将这个节点关闭,然后我们退后 \(x\) 步。接下来分情况讨论,如果设备数量大于 \(x\) 个,那么在后退的过程中,我们会一直退回到节点 \(1\),并且途径所有设备都被打开了(因为这是我们打开的);但是如果节点数量小于等于 \(x\) 个,那么整个图的状态就是全部打开,但是在我们最后一个点(也就是 \(x+1\))是关闭的,由于节点数量小于等于 \(x\) 个,那么我们就保证后退 \(x\) 的过程中会至少绕一圈,这时候我们就会第二次发现一个设备是关闭的,那么此时走的步数就是答案。
例,如图:

我们假设加粗节点为设备打开,未加粗节点为设备关闭,此时我们在节点 \(1\) 上。假设 \(x\) 为 \(4\),那么节点 \(1\) 到 \(5\) 都会是打开状态,然后我们处在节点 \(5\) 上,并且关闭了节点 \(5\)。这时候我们退后 \(4\) 步,一直走完这 \(4\) 步到节点 \(1\),我们都没有遇到关闭的设备,所以设备数一定大于 \(x\)。假设 \(x\) 是 \(8\),那么我们最终处于节点 \(3\) 上,并且除了节点 \(3\) 的是关闭的,其他都是打开的。这时候我们一直后退,一直后退了 \(6\) 步后,我们发现设备是关闭的,显然,这时候我们在节点 \(3\) 上。这意味着我们绕着整个矿场走了一圈,设备数也就是答案就是 \(6\)。
现在考虑一个一个验证哪些答案来解题。通常情况下,对答案二分是最好的方案,但是看这道题,虽然我们能确认上下界,但是假如环长为 \(10^5\) 米,但一共只有 \(2\) 个设备,那么如果我们向前走 \(5 \times 10^4\) 个设备,我们就走过了极多的距离。
我们发现一旦枚举的 \(x\) 大于等于设备数量,那么就可以终止交互并且输出,而只要 \(x\) 小于设备数量,我们最多只会走 \(1\) 圈的距离。那么考虑从小到大枚举 \(x\),采用倍增的优化,刚开始 \(x=1\),后来每次 \(x\) 都乘 \(2\),依次 \(check\),最后得出答案。最多只会 \(check\) \(\log(n)\) 次。
Show Code
#include
#define int long long
using namespace std;
auto mread = [](){int x; scanf("%lld", &x); return x;};
int n = mread(), now = mread();
void print(int x){
cout << "? " << x << "\n";
cout.flush();
if(x == 1 || x == 2)
now = mread();
return;
}
bool check(int x){
for(int i = 1; i <= x; i ++){
if(now == 0)
print(3);
print(1);
}
if(now == 1)
print(3);
int ans = 0;
for(int i = 1; i <= x; i ++){
print(2);
ans ++;
if(now == 0){
cout << "! " << ans;
cout.flush();
return 1;
}
}
return 0;
}
signed main(){
int sum = 1;
while(1){
if(check(sum))
break;
sum *= 2;
}
return 0;
}

浙公网安备 33010602011771号