【题解】CF2067D Object Identification

CF2067D - Object Identification

题目大意

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

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

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

  • 若这个图是一个有向图,则你的询问 i,j 为从结点 i 到结点 j 的最短路径长度。
  • 若这个图是一个二维坐标系,则你的询问 i,j 为从点 (xi,yi)(xj,yj) 的曼哈顿距离 |xixj|+|yiyj|

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

思路

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

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

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

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

如此一番操作后,我们还剩下一次询问机会,并且如果这个时候还没有得出答案,则只剩下一种情况:d(a,b)=n1
再仔细观察可以发现,在有 n 个结点的有向图中,每个结点的出度均为 1,两点最短路径长为 n1,那这时候先不管终点 b 的那一条出边,此时这个图,一定是一个以 a 为起点,b 为重点的链!
此时,我们仅剩一条边没有连接,也就是数字 n 对应的 b 结点,我们注意到题目给出的数据范围 n3,因此,b 结点要么向其他结点连边,无法到达结点 a,要么向 a 连边,但 d(b,a)=1d(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 @   天天超方的  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示