CF1586D. Omkar and the Meaning of Life 题解 纪念一下第一道AC的交互题

题目链接:https://codeforces.com/contest/1586/problem/D

题目大意:

有一个 \(1 \sim n\) 的排列 \(p\),你最多询问 \(2n\) 轮,每轮你可以输入一个长度为 \(n\) 的数列 \(a\),要求 \(1 \le a_i \le n\)(但 \(a\) 不需要是一个 \(1 \sim n\) 的排列)。

对于你每次输入的数列 \(a\),程序会产生一个数列 \(s\),其中 \(s_i = p_i + a_i\),然后程序会返回给你一个出现过至少两次的 \(s_k\) 对应的最小坐标 \(k\)。若没有这个坐标 \(k\) 则返回 \(0\)

要求在不超过 \(2n\) 轮求出原始的数列 \(p\)

解题思路:

\(p(1 \le p \le n)\) 轮共两次询问:

  • 第一次询问,除了 \(a_p = 2\) 以外,其他所有 \(a_i = 1\),设程序返回的整数是 \(q_1\),则:若 \(q_1 \neq 0\)\(q_1 \neq p\),说明第 \(p\) 个位置上面的数比第 \(q_1\) 个位置上面的数小 \(1\),则从 \(p\) 指向 \(q_1\) 一条边(\(p \rightarrow q_1\));
  • 第二次询问,除了 \(a_p = 1\) 以外,其他所有 \(a_i = 2\),设程序返回的整数是 \(q_2\),则:若 \(q_2 \neq 0\)\(q_2 \neq p\),说明第 \(p\) 个位置上面的数比第 \(q_1\) 个位置上面的数大 \(1\),则从 \(q-2\) 指向 \(p\) 一条边(\(q_2 \rightarrow p\))。

然后这 \(2n\) 轮操作之后会发现 —— 形成了一条链,根据链还原出每个数的位置即可。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
bool in[maxn];
int n, q1, q2, g[maxn], x;

void ask1(int p) {
    printf("?");
    for (int i = 1; i <= n; i ++) printf(" %d", (i == p) ? 2 : 1);
    puts("");
    fflush(stdout);
}

void ask2(int p) {
    printf("?");
    for (int i = 1; i <= n; i ++) printf(" %d", (i == p) ? 1 : 2);
    puts("");
    fflush(stdout);
}

int val[maxn];
bool vis[maxn];

int main() {
    scanf("%d", &n);
    for (int p = 1; p <= n; p ++) {
        ask1(p);
        scanf("%d", &q1);
        if (q1 != 0 && q1 != p) {
            g[p] = q1;
        }
        ask2(p);
        scanf("%d", &q2);
        if (q2 != 0 && q2 != p) {
            g[q2] = p;
        }
    }
    for (int i = 1; i <= n; i ++) {
        if (!g[i]) x = i;
    }
    for (int i = 1; i <= n; i ++) vis[ g[i] ] = true;
    for (int i = 1; i <= n; i ++) if (!vis[i]) { x = i; break; }
    for (int i = 1; i <= n; i ++) {
        val[x] = i;
        x = g[x];
    }
    printf("!");
    for (int i = 1; i <= n; i ++) printf(" %d", val[i]);
    return 0;
}
posted @ 2021-10-17 21:27  quanjun  阅读(146)  评论(0编辑  收藏  举报