[CF] A. Object Identification - Codeforces Round 1004 (Div. 1)

https://codeforces.com/contest/2066/problem/A

题目大意

给出 $n$ 长的数组 $x$ 和某个隐藏的确定的数组 $y$。保证 $x_i \neq y_i, \forall i$ 且 $(x_i, y_i) \neq (x_j, y_j), \forall i \neq j$ 。对于每组数据,背后有一个隐藏的对象是以下两种之一:

  1. Obj A: 一个 $n$ 个点的有向图当且仅当存在有向边 $(x_i, y_i)$
  2. Obj B: $n$ 个点在二维平面上,每个点坐标为 $(x_i, y_i)$。

可以最多进行两次询问,每次询问给出 $i, j$:

如果是 Obj A 则返回 $i -> j$ 的最短路,不存在路径则返回 $0$。

如果是 Obj B 则返回 $|x_i - x_j| + |y_i - y_j|$。

两次询问后判断隐藏的对象是哪一种。

$3 \le n \le 2 \times 10^5$。

$1 \le x_i, y_i \le n$。

简要题解

观察:

  1. $x$ 不可能所有元素都相同,否则由 $x_i \neq y_i$ 则 $y_i$ 只有 $n - 1$ 个值可以选,根据鸽巢原理必有 $(x, y_i) = (x, y_j)$ 存在。
  2. 如果是 Obj A 则答案不会超过 $n - 1$。
  3. 如果是 Obj B 则答案不会为 $0$(因为点彼此不同,则其曼哈顿距离不会为 $0$)。
  4. 如果是 Obj A 且某值 $a$ 没有出现在 $x_i$ 中,则其到其他所有点没有边,则询问总为 $0$。
  5. 如果是 Obj B 则,对于 $x_i = 1, x_j = n$ 答案不会小于 $n - 1$。

有了这些观察,我们发现,如果 $x_i$ 有重复值,则必定有没有出现的值 $a$,那么我们只要用 $a$ 和任意其他 $b$ 做一次询问,则我们由观察 $3, 4$ 即可判断出是哪一种情况。

如果 $x_i$ 没有重复值,则 $x_i$ 是 $1 \sim n$ 的一个排列,则总有 $1$ 和 $n$ 存在,这时我们询问 $(1, n)$ 与 $(n, 1)$ 若任意一个小于 $n - 1$ 则根据观察 $5$ 其是 Obj A,若其大于 $n - 1$ 则其是 Obj B。

最后如何判断这两个值都是 $n - 1$ 的情况呢?注意到 $n \ge 3$,此时若 $1 -> n$ 和 $n -> 1$ 都有 $n - 1$ 条边,则总有 $2n - 2 > n, \forall n \ge 3$,即这不可能是 Obj A。

(这题写起来好烦,很容易注意到一些没用的东西,比如观察 1 似乎完全没用到!)

复杂度

$T$:$O(n)$

$S$:$O(n)$

代码实现

#include <bits/stdc++.h>
using namespace std;

int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();

using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;

template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) { 
    return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) { 
    return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z 
    if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
    if(x<z) { y=x; x=z; } else if(y<z) y=z; }

// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());

/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/

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

void solve() {
  int n; cin >> n;
  vector<int> a(n);
  for (int& i : a) cin >> i;

  vector<vector<int>> pos(n + 1);

  for (int i = 0; i < n; i++) {
    pos[a[i]].push_back(i + 1);
  }

  bool has_two = false;
  int x, y;
  for (int i = 1; i <= n; i++) {
    if (pos[i].size() >= 2) {
      has_two = true;
    }
  }

  if (has_two) {
    for (int i = 1; i <= n; i++) {
      if ((int)pos[i].size() == 0) {
        x = i;
        break;
      }
    }
    for (int i = 1; i <= n; i++) {
      if (i != x) {
        y = i;
        break;
      }
    }

    int ans = query(x, y);
    if (ans) {
      cout << "! B" << endl;
    } else {
      cout << "! A" << endl;
    }
    return;
  }

  // all different!
  for (int i = 0; i < n; i++) {
    if (a[i] == 1) {
      x = i + 1;
    }
    if (a[i] == n) {
      y = i + 1;
    }
  }

  if (query(x, y) < n - 1 || query(y, x) < n - 1) {
    cout << "! A" << endl;
  } else {
    cout << "! B" << endl;
  }
}

int main() {
  int t = 1; 
  cin >> t;
  while (t--) {
    solve();
  }
  return 0;
}
Prev: [CF] B. Minimization - Codeforces Round 317 [AimFund Thanks-Round] (Div. 1)
Next: [CF] E. Vasya and Binary String - Educational Codeforces Round 59 (Rated for Div. 2)