题解:CF1838F Stuck Conveyor

· · 题解

题解:CF1838F Stuck Conveyor

前置知识:IO 交互题。

题意简述

一个 n \times n 的正方形区域,有 n^2 个传送带方块,你每次可以指定每个方块的方向,并且在其中任意一个位置投放一个物品,物品会沿方块所指的方向运动,你最后会得知方块出来的位置 (x , y) 或方块进入了死循环 (-1 , -1)。但是有一个方块不会受到你的控制,而是会固定朝向一个方向,请你在 25 次分配内找到这个方块以及它的朝向。

解题思路

交互题构造二分

打表找规律,可以发现:

所以我们可以设计出类似蛇形的地图:

>>>>v
v<<<<
>>>>v
v<<<<
>>>>v

^<<<<
>>>>^
^<<<<
>>>>^
^<<<<

此时可以发现这两张地图非常巧妙的让所有正确的情况在两张图里面没有交集,即:如果一个点在第一张图成功,那么一定是:

换句话说,无论如何也有一张图会让它死循环或出界。

生成第一张图:

void zmp()
{
    for(int i = 1 ; i <= n ; i++)
    {
        if(i % 2)
        {
            for(int j = 1 ; j < n ; j++)
            {
                cout << '>';
            }
            cout << 'v' << endl;
        }
        else
        {
            cout << 'v';
            for(int j = 1 ; j < n ; j++)
            {
                cout << '<';
            }
            cout << endl;
        }
    }
    return;
}

生成第二张图:

void fmp()
{
    for(int i = 1 ; i <= n ; i++)
    {
        if(i % 2)
        {
            cout << '^';
            for(int j = 1 ; j < n ; j++)
            {
                cout << '<';
            }
            cout << endl;
        }
        else
        {
            for(int j = 1 ; j < n ; j++)
            {
                cout << '>';
            }
            cout << '^' << endl;
        }
    }
    return;
}

此时,问题就被划分为了两个子问题出界、死循环

出界问题相对简单,直接判掉就可以了。

if(没到达目标位置且没有输入 -1 -1)
{
    if(x < 1)
    {
        x++;
        cout << "! " << x << ' ' << y << " ^" << endl;
        return 0;
    }
    if(x > n)
    {
        x--;
        cout << "! " << x << ' ' << y << " v" << endl;
        return 0;
    }
    if(y < 1)
    {
        y++;
        cout << "! " << x << ' ' << y << " <" << endl;
        return 0;
    }
    if(y > n)
    {
        y--;
        cout << "! " << x << ' ' << y << " >" << endl;
        return 0;
    }
}

死循环的情况相对复杂,需要先确定哪张图能够导致死循环,然后再做下一步处理。确定之后就最坏情况还剩 23 次询问,恰好我们需要在最多 10^4 个数里面找,联想 \log 级别的算法,联想二分算法

我们可以把矩阵转换成一条链

这条链就是物品在没有坏方块时要经过的道路,也就相当于上面的蛇形矩阵。链中有仅有一次每个位置,即链长是 n^2

由于物品能不能正常离开满足单调性,所以可以在链上二分

换成代码来说,如果系统返回 x , y[l , r] 为链上可能为答案的区间,那么可以用以下方法进行检查:

mid = (l + r) / 2;
if(x == -1 && y == -1)
{
    ans = mid;
    l = mid + 1;
}
else
{
    r = mid - 1;
}

通过 \log \text{链长} 的询问次数找到到底是哪个点出现了问题。

此时,答案即为链上答案点对应矩阵中的 x,y 坐标。

可是丧心病狂的出题人还要我们输出方向。

我们可以使用“冲浪”矩阵:

对于第一组地图构造:

vvvvv
vvvvv
vvvvv
vvvvv
vvvvv

对于第二组地图构造:

^^^^^
^^^^^
^^^^^
^^^^^
^^^^^

这题就这么用特判堆过了。

期望最大询问次数:1 + 1 + 14 + 1 = 17

### 参考代码 ```cpp #include<bits/stdc++.h> using namespace std; struct p { int x , y; }; int n , x , y , l , r , mid , ans; vector<p> v; char c; void zmp() { for(int i = 1 ; i <= n ; i++) { if(i % 2) { for(int j = 1 ; j < n ; j++) { cout << '>'; } cout << 'v' << endl; } else { cout << 'v'; for(int j = 1 ; j < n ; j++) { cout << '<'; } cout << endl; } } return; } void fmp() { for(int i = 1 ; i <= n ; i++) { if(i % 2) { cout << '^'; for(int j = 1 ; j < n ; j++) { cout << '<'; } cout << endl; } else { for(int j = 1 ; j < n ; j++) { cout << '>'; } cout << '^' << endl; } } return; } int main() { cin >> n; cout << "? 1 1" << endl; zmp(); cin >> x >> y; if(x != -1 && y != -1) { if(n % 2) { if(x != n + 1 || y != n) { if(x < 1) { x++; cout << "! " << x << ' ' << y << " ^" << endl; return 0; } if(x > n) { x--; cout << "! " << x << ' ' << y << " v" << endl; return 0; } if(y < 1) { y++; cout << "! " << x << ' ' << y << " <" << endl; return 0; } if(y > n) { y--; cout << "! " << x << ' ' << y << " >" << endl; return 0; } } } else { if(x != n + 1 || y != 1) { if(x < 1) { x++; cout << "! " << x << ' ' << y << " ^" << endl; return 0; } if(x > n) { x--; cout << "! " << x << ' ' << y << " v" << endl; return 0; } if(y < 1) { y++; cout << "! " << x << ' ' << y << " <" << endl; return 0; } if(y > n) { y--; cout << "! " << x << ' ' << y << " >" << endl; return 0; } } } if(n % 2) { cout << "? " << n << ' ' << n << endl; fmp(); cin >> x >> y; if(x != -1 && y != -1) { if(x < 1) { x++; cout << "! " << x << ' ' << y << " ^" << endl; return 0; } if(x > n) { x--; cout << "! " << x << ' ' << y << " v" << endl; return 0; } if(y < 1) { y++; cout << "! " << x << ' ' << y << " <" << endl; return 0; } if(y > n) { y--; cout << "! " << x << ' ' << y << " >" << endl; return 0; } } else { for(int i = n ; i >= 1 ; i--) { if(i % 2) { for(int j = n ; j >= 1 ; j--) { v.push_back(p{i , j}); } } else { for(int j = 1 ; j <= n ; j++) { v.push_back(p{i , j}); } } } l = 0; r = v.size() - 1; while(l <= r) { mid = (l + r) / 2; cout << "? " << v[mid].x << ' ' << v[mid].y << endl; fmp(); cin >> x >> y; if(x == -1 && y == -1) { ans = mid; l = mid + 1; } else { r = mid - 1; } } cout << "? " << n << ' ' << v[ans].y << endl; for(int i = 1 ; i <= n ; i++) { for(int j = 1 ; j <= n ; j++) { cout << '^'; } cout << endl; } cin >> x >> y; if(x == -1 && y == -1) { cout << "! " << v[ans].x << ' ' << v[ans].y << " v" << endl; return 0; } else { if(v[ans].y < y) { c = '>'; } else { c = '<'; } cout << "! " << v[ans].x << ' ' << v[ans].y << ' ' << c << endl; return 0; } } } else { cout << "? " << n << ' ' << 1 << endl; fmp(); cin >> x >> y; if(x != -1 && y != -1) { if(x < 1) { x++; cout << "! " << x << ' ' << y << " ^" << endl; return 0; } if(x > n) { x--; cout << "! " << x << ' ' << y << " v" << endl; return 0; } if(y < 1) { y++; cout << "! " << x << ' ' << y << " <" << endl; return 0; } if(y > n) { y--; cout << "! " << x << ' ' << y << " >" << endl; return 0; } } else { for(int i = n ; i >= 1 ; i--) { if(i % 2) { for(int j = n ; j >= 1 ; j--) { v.push_back(p{i , j}); } } else { for(int j = 1 ; j <= n ; j++) { v.push_back(p{i , j}); } } } l = 0; r = v.size() - 1; while(l <= r) { mid = (l + r) / 2; cout << "? " << v[mid].x << ' ' << v[mid].y << endl; fmp(); cin >> x >> y; if(x == -1 && y == -1) { ans = mid; l = mid + 1; } else { r = mid - 1; } } cout << "? " << n << ' ' << v[ans].y << endl; for(int i = 1 ; i <= n ; i++) { for(int j = 1 ; j <= n ; j++) { cout << '^'; } cout << endl; } cin >> x >> y; if(x == -1 && y == -1) { cout << "! " << v[ans].x << ' ' << v[ans].y << " v" << endl; return 0; } else { if(v[ans].y < y) { c = '>'; } else { c = '<'; } cout << "! " << v[ans].x << ' ' << v[ans].y << ' ' << c << endl; return 0; } } } } else { for(int i = 1 ; i <= n ; i++) { if(i % 2) { for(int j = 1 ; j <= n ; j++) { v.push_back(p{i , j}); } } else { for(int j = n ; j >= 1 ; j--) { v.push_back(p{i , j}); } } } l = 0; r = v.size() - 1; while(l <= r) { mid = (l + r) / 2; cout << "? " << v[mid].x << ' ' << v[mid].y << endl; zmp(); cin >> x >> y; if(x == -1 && y == -1) { ans = mid; l = mid + 1; } else { r = mid - 1; } } cout << "? 1 " << v[ans].y << endl; for(int i = 1 ; i <= n ; i++) { for(int j = 1 ; j <= n ; j++) { cout << 'v'; } cout << endl; } cin >> x >> y; if(x == -1 && y == -1) { cout << "! " << v[ans].x << ' ' << v[ans].y << " ^" << endl; return 0; } else { if(v[ans].y < y) { c = '>'; } else { c = '<'; } cout << "! " << v[ans].x << ' ' << v[ans].y << ' ' << c << endl; return 0; } } return 0; } ```