【题解】CF2067D Object Identification

CF2067D - Object Identification

题目大意

有一个对你公开的 \(x\) 数组和一个对你隐藏的 \(y\) 数组,保证没有任何两个相同的 \(\{x_i, y_i\}\),并且 \(x_i \neq y_i\),对于这两个数组,有以下两种可能:

  • A:一个有 \(n\) 个结点的有向图,每一条边从 \(x_i\) 指向 \(y_i\)
  • B:一个二维坐标系中的一些点,每个点的坐标为 \((x_i, y_i)\)

你可以对这个图进行询问:

  • 若这个图是一个有向图,则你的询问 \(i, j\) 为从结点 \(i\) 到结点 \(j\) 的最短路径长度。
  • 若这个图是一个二维坐标系,则你的询问 \(i, j\) 为从点 \((x_i, y_i)\)\((x_j, y_j)\) 的曼哈顿距离 \(|x_i - x_j| + |y_i - y_j|\)

现在,你有最多 \(2\) 次询问机会,来确定这个图到底是一个有向图,还是一个二维坐标系。

思路

很好的一个交互题,一看题目,居然只给 \(2\) 次询问?!有意思。
要区分这两种图,我们要从二者分别的特征入手:

  • 有向图:所有的边是单向的,不可反向行走,因此两个结点正着走和反着走的距离不一定相同。
  • 二维坐标系:所有的点两两之间的曼哈顿距离是相同的,因此正着走和反着走的距离都相同,并在该题目条件下,不存在重合的两个点,因此距离一定不为 \(0\)

因此,这里有一个初步的思路,挑选两个数字,正着问一次,反着问一次,判断两次询问的距离是否相同。
但很遗憾,就算是有向图,正着和反着的距离也有可能会相同(如下图中的 \(1\)\(3\))。
有向图中两点距离相同
因此,若问出来距离相同,仍然无法判断为有向图还是二维坐标系。
那么,有没有什么更特殊的点,能直接作为判断标志呢?
有的,在有向图(不含自环)中,若一个结点的出度为 \(0\),也就是这个结点没有任何的出边,那么该结点无法到达任何其他结点,即距离为 \(0\)
也就是说,如果有 \(1\)\(n\) 中的某一个数在数组 \(x\) 中一次也没有出现,那么这个结点的出度一定为 \(0\),此时,只需要把它作为询问的第一个数,再任意询问另一个数,如果距离为 \(0\),则是有向图,如果距离不为 \(0\),则是二维坐标系。

当然,不难发现,还有一种情况会发生,那就是 \(1\)\(n\) 中的所有数字均在 \(x\) 数组中出现,并且一定只会出现一次(如果有某个数字出现两次及以上,则一定会有一个数字不出现)。
在这种情况中,如果是有向图,则不存在出度为 \(0\) 的结点,又应该如何判断呢?
这里我们就要用到我们 \(x\) 数组的信息了,由于 \(x\) 数组是已知的,再结合曼哈顿距离的定义,我们可以得出下面这个显而易见的结论:\(d(i, j) \geq |x_j - x_i|\)
因此,我们可以用这个不等式来区分有向图和二维坐标系,那选用哪两个数字来询问呢?
这里我们选取 \(1\)\(n\)\(x\) 数组中的位置 \(a,b\),因为 \(d(a, b) \geq n - 1\),而我们的有向图中一共有 \(n\) 个结点,任意两点的距离最大值只可能达到 \(n - 1\),因此如果询问 \(1\)\(n\)\(x\) 数组中的位置后,如果得到的答案 \(> n - 1\),则一定是二维坐标系,如果得到的答案 \(< n - 1\),则一定是有向图。

如此一番操作后,我们还剩下一次询问机会,并且如果这个时候还没有得出答案,则只剩下一种情况:\(d(a, b) = n - 1\)
再仔细观察可以发现,在有 \(n\) 个结点的有向图中,每个结点的出度均为 \(1\),两点最短路径长为 \(n - 1\),那这时候先不管终点 \(b\) 的那一条出边,此时这个图,一定是一个以 \(a\) 为起点,\(b\) 为重点的链!
此时,我们仅剩一条边没有连接,也就是数字 \(n\) 对应的 \(b\) 结点,我们注意到题目给出的数据范围 \(n \geq 3\),因此,\(b\) 结点要么向其他结点连边,无法到达结点 \(a\),要么向 \(a\) 连边,但 \(d(b, a) = 1 \neq d(a, b)\),所以一定是有向图。
因此这时只需要再询问一次 \(d(b, a)\),判断 \(d(a, b)\) 是否等于 \(d(b, a)\),若等于,则是二维坐标系,否则是有向图,总询问次数 \(2\) 次,通过!

AC CODE

const int N = 2e5 + 9;
int a[N];
int p[N];
int cnt[N];

int ask(int x, int y) {
    cout << "? " << x << ' ' << y << endl;
    int op;cin >> op;
    return op;
}

void solve()
{
    int n;cin >> n;
    for(int i = 1;i <= n;i ++)cin >> a[i];
    for(int i = 1;i <= n;i ++)cnt[i] = 0;

    for(int i = 1;i <= n;i ++)cnt[a[i]] ++, p[a[i]] = i;

    for(int i = 1;i <= n;i ++) {
        if(!cnt[i]) {
            if(ask(i, (i % n + 1)) == 0) {
                cout << "! A" << endl;
            } else {
                cout << "! B" << endl;
            }
            return;
        }
    }

    int ck = ask(p[1], p[n]);

    if(ck >= n) {
        cout << "! B" << endl;
        return;
    }

    if(ck < n - 1) {
        cout << "! A" << endl;
        return;
    }

    int dk = ask(p[n], p[1]);

    if(dk == ck) {
        cout << "! B" << endl;
    } else {
        cout << "! A" << endl;
    }
}
posted @ 2025-02-23 18:21  天天超方的  阅读(59)  评论(0)    收藏  举报