CodeforcesRound#749(Div.1.2basedonTechnocup2022EliminationRound1)-D.Omkar and the Meaning of Life-题解

Codeforces Round #749 (Div. 1 + Div. 2, based on Technocup 2022 Elimination Round 1) - D. Omkar and the Meaning of Life

传送门
Time Limit: 2 seconds
Memory Limit: 256 megabytes

Problem Description

It turns out that the meaning of life is a permutation p 1 , p 2 , … , p n p1,p2,…,pn p1,p2,,pn of the integers 1 , 2 , … , n ( 2 ≤ n ≤ 100 ) 1,2,…,n (2≤n≤100) 1,2,,n(2n100). Omkar, having created all life, knows this permutation, and will allow you to figure it out using some queries.

A query consists of an array a 1 , a 2 , … , a n a1,a2,…,an a1,a2,,an of integers between 1 1 1 and n n n. a is not required to be a permutation. Omkar will first compute the pairwise sum of a a a and p p p, meaning that he will compute an array s s s where s j = p j + a j s_j=p_j+a_j sj=pj+aj for all j = 1 , 2 , … , n j=1,2,…,n j=1,2,,n. Then, he will find the smallest index k k k such that sk occurs more than once in s s s, and answer with k k k. If there is no such index k k k, then he will answer with 0 0 0.

You can perform at most 2 n 2n 2n queries. Figure out the meaning of life p p p.

Interaction

Start the interaction by reading single integer n ( 2 ≤ n ≤ 100 ) n (2≤n≤100) n(2n100) — the length of the permutation p p p.

You can then make queries. A query consists of a single line “ ?   a 1   a 2   …   a n ?\ a_1\ a_2\ …\ a_n ? a1 a2  an ( 1 ≤ a j ≤ n ) (1≤a_j≤n) (1ajn).

The answer to each query will be a single integer k k k as described above ( 0 ≤ k ≤ n ) (0≤k≤n) (0kn).

After making a query do not forget to output end of line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:

fflush(stdout) or cout.flush() in C++;
System.out.flush() in Java;
flush(output) in Pascal;
stdout.flush() in Python;

see documentation for other languages.

To output your answer, print a single line “ !   a 1   a 2   …   a n !\ a_1\ a_2\ …\ a_n ! a1 a2  an” then terminate.

You can make at most 2 n 2n 2n queries. Outputting the answer does not count as a query.

Hack Format

To hack, first output a line containing n ( 2 ≤ n ≤ 100 ) n (2≤n≤100) n(2n100), then output another line containing the hidden permutation p 1 , p 2 , … , p n p_1,p_2,…,p_n p1,p2,,pn of numbers from 1 1 1 to n n n.

Sample Input

5

2

0

1

Sample Onput


? 4 4 2 3 2

? 3 5 1 5 5

? 5 2 4 3 1

! 3 2 1 5 4

Note

In the sample, the hidden permutation p p p is [ 3 , 2 , 1 , 5 , 4 ] [3,2,1,5,4] [3,2,1,5,4]. Three queries were made.

The first query is a = [ 4 , 4 , 2 , 3 , 2 ] a=[4,4,2,3,2] a=[4,4,2,3,2]. This yields s = [ 3 + 4 , 2 + 4 , 1 + 2 , 5 + 3 , 4 + 2 ] = [ 7 , 6 , 3 , 8 , 6 ] s=[3+4,2+4,1+2,5+3,4+2]=[7,6,3,8,6] s=[3+4,2+4,1+2,5+3,4+2]=[7,6,3,8,6]. 6 6 6 is the only number that appears more than once, and it appears first at index 2 2 2, making the answer to the query 2 2 2.

The second query is a = [ 3 , 5 , 1 , 5 , 5 ] a=[3,5,1,5,5] a=[3,5,1,5,5]. This yields s = [ 3 + 3 , 2 + 5 , 1 + 1 , 5 + 5 , 4 + 5 ] = [ 6 , 7 , 2 , 10 , 9 ] s=[3+3,2+5,1+1,5+5,4+5]=[6,7,2,10,9] s=[3+3,2+5,1+1,5+5,4+5]=[6,7,2,10,9]. There are no numbers that appear more than once here, so the answer to the query is 0.

The third query is a = [ 5 , 2 , 4 , 3 , 1 ] a=[5,2,4,3,1] a=[5,2,4,3,1]. This yields s = [ 3 + 5 , 2 + 2 , 1 + 4 , 5 + 3 , 4 + 1 ] = [ 8 , 4 , 5 , 8 , 5 ] s=[3+5,2+2,1+4,5+3,4+1]=[8,4,5,8,5] s=[3+5,2+2,1+4,5+3,4+1]=[8,4,5,8,5]. 5 5 5 and 8 8 8 both occur more than once here. 5 5 5 first appears at index 3 3 3, while 8 8 8 first appears at index 1 1 1, and 1 < 3 1<3 1<3, making the answer to the query 1 1 1.

Note that the sample is only meant to provide an example of how the interaction works; it is not guaranteed that the above queries represent a correct strategy with which to determine the answer.


题目大意

给你一个 n n n,这组数据其实是隐藏了 n n n的一个全排列。你可以进行最多 2 n 2n 2n次询问,每次输入一个新的序列,让隐藏的序列一一对应临时加上这个新的序列,之后把有相同的值的元素中,最小的下标返回给你。

让你通过询问,得出隐藏的序列。

比如样例隐藏了序列 [ 3 , 2 , 1 , 5 , 4 ] [3,2,1,5,4] [3,2,1,5,4],你进行了一次询问并让隐藏序列一一对应地临时加上你给的序列 [ 4 , 4 , 2 , 3 , 2 ] [4,4,2,3,2] [4,4,2,3,2]. 那么相加产生的临时序列是 [ 3 + 4 , 2 + 4 , 1 + 2 , 5 + 3 , 4 + 2 ] = [ 7 , 6 , 3 , 8 , 6 ] [3+4,2+4,1+2,5+3,4+2]=[7,6,3,8,6] [3+4,2+4,1+2,5+3,4+2]=[7,6,3,8,6]. 6 6 6 不只一个,所有有相同值的元素集合是: [ 第 2 个 元 素 , 第 5 个 元 素 ] [第2个元素,第5个元素] [25],其中下标最小的是第二个元素,它的下标是 2 2 2。因此,你得到了询问的结果: 2 2 2

解题思路

其实每次询问可以把其中一个数 a a a加一,其他所有数都加2。那么如果有 b = a − 1 b=a-1 b=a1,那么因为 a a a 加了 1 1 1 b b b 加了 2 2 2 ,所以 a a a b b b加上临时序列后的值就会相同,因此就会返回这两个数中下标最小的元素。如果很荣幸 b = a − 1 b=a-1 b=a1 b b b的前面,那么我们就得到了比 a a a 1 1 1的那个数的下标。

同理,只把 a a a加上 2 2 2,其他元素都加上 1 1 1,就可以令 a a a a + 1 a+1 a+1相等(如果存在的话)。如果 a + 1 a+1 a+1 a a a的前面,那么我们就能得到比 a a a 1 1 1的数的下标。

正好 2 ∗ n 2*n 2n次询问,我们可以得到 n − 1 n-1 n1个“大1关系”,即得到了 n − 1 n-1 n1对相差为 1 1 1的数。

因为没有 0 0 0,所以不存在比 1 1 1 1 1 1的数,由此特殊条件我们可以判断出最小的数 1 1 1所在的位置。之后依据 n − 1 n-1 n1对“大1关系”,就可以还原出原始数组的样子。


AC代码

/*
 * @Author: LetMeFly
 * @Date: 2021-10-17 20:10:26
 * @LastEditors: LetMeFly
 * @LastEditTime: 2021-10-19 00:07:29
 */ // LastEditTime: 2021-10-17 20:59:28
#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;
struct Conditions {
    int l, r; // 下标为l的元素比下标为r的大1
    // bool xd;
} cond[1010];
int sx[1010] = {0}; // sx[i]用来记录下标为i的数是否当过“比其他数大1的数”。如果没有,那么下标为i的数就是1
int Next[1010]; // Next[i]代表下标为i的数+1的数的下标
int ans[1010]; // 答案。ans[i]代表下标为i的数的真实隐藏的数
int main() {
    int n;
    cin >> n; // n个数
    int cnt = 0; // 现在又了cnt个“大1关系”
    for (int i = 1; i <= n; i++) {
        printf("? "); // 询问
        for (int j = 1; j <= n; j++) {
            if (j != i) printf("2 "); // 这个数之外的数都加2
            else printf("1 "); // 这个数+1
        }
        puts(""); // 换行
        fflush(stdout); // 清空缓冲区
        int answer; // 这个询问的回答
        cd(answer); // scanf("%d", &answer);
        if (answer != 0 && answer < i) { // answer=0代表加上后没有相同的数,answer=i说明要找的数在i后面
            cond[cnt].l = answer; // 回答的数的下标
            cond[cnt].r = i; // 比只加了1的数 小1
            // cond[cnt].xd = true;
            cnt++; // 已有的“大1关系”的数++
        }
        printf("? "); // 询问
        for (int j = 1; j <= n; j++) {
            if (j != i) printf("1 "); // 其他数+2
            else printf("2 "); // 这个数+1
        }
        puts(""); // 换行
        fflush(stdout); // 清空缓冲区
        cd(answer); // 同上
        if (answer != 0 && answer < i) {
            cond[cnt].l = i;
            cond[cnt].r = answer;
            // cond[cnt].xd = false;
            cnt++;
        }
    }
    for (int i = 0; i < cnt; i++) {
        Next[cond[i].l] = cond[i].r; // 下标为cond[i].l的数 +1得到的数 的下标是cond[i].r
        sx[cond[i].r]++; // cond[i].r当过“比其他数大1的数”
    }
    int one; // 1所在的下标
    for (int i = 1; i <= n; i++) {
        if (!sx[i]) { // 这个数没当过“比其他数大1的数”
            one = i; // 1就是它了
            break;
        }
    }
    for (int i = 1; i <= n; i++) { // 一共要还原n个数
        ans[one] = i; // 答案(原始的隐藏的数)
        one = Next[one]; // 比它大1的数
    }
    printf("! "); // 输出最终答案
    for (int i = 1; i <= n; i++) {
        printf("%d ", ans[i]);
    }
    puts(""); // 换行
    return 0;
}

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/120837226

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tisfy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值