矿场设备问题 题解

题目链接

这道题是基于验证答案的。直接对设备数量求解并不简单,但是通过对一个答案是否正确进行验证,就可以解决这道题。

我们首先假设答案是 \(x\),考虑在什么情况下这个答案合法。

我们考虑一个策略,就是首先向前走 \(x\) 步,并且途径设备全部打开。最后我们走到第 \(x+1\) 个节点,再将这个节点关闭,然后我们退后 \(x\) 步。接下来分情况讨论,如果设备数量大于 \(x\) 个,那么在后退的过程中,我们会一直退回到节点 \(1\),并且途径所有设备都被打开了(因为这是我们打开的);但是如果节点数量小于等于 \(x\) 个,那么整个图的状态就是全部打开,但是在我们最后一个点(也就是 \(x+1\))是关闭的,由于节点数量小于等于 \(x\) 个,那么我们就保证后退 \(x\) 的过程中会至少绕一圈,这时候我们就会第二次发现一个设备是关闭的,那么此时走的步数就是答案。

例,如图:
image
我们假设加粗节点为设备打开,未加粗节点为设备关闭,此时我们在节点 \(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;
}
posted @ 2024-02-12 16:55  cndark_moon  阅读(17)  评论(0)    收藏  举报