题目内容
样例输入
5
2
0
1
样例输出
? 4 4 2 3 2
? 3 5 1 5 5
? 5 2 4 3 1
! 3 2 1 5 4
题解
首先,题目有一个很难解决的痛点,就是他只会返回最前面的那个下标。那我们就不妨倒着做,从后往前处理。
先考虑最后一个数字。我们如果发出类似 "1 1 1 1 2" 这样的询问,就相当于把最后一个数字抬高了 \(1\), 相应地,改成 "1 1 1 1 3" 就是把最后一个数字抬高了 \(2\)。
我们先不断抬高最后一个数字,期间也记录下来他和前面某些数字的关系(前面这个数字比最后一位大 \(k\)),这样我们在知道最后一个数字之后就能直接推出有关系的数字,减少询问次数。
可以发现,最后一个数字被抬高某个 \(k\) 之后,交互器一定会返回 \(0\),因为此时他比最大值 \(n\) 大了 \(1\)。那么这个时候,我们就找出来了最大值 \(n\) 的位置以及最后一个数字的值。(二者当然有可能重合)。
在这里,假设我们花了 \(M\) 次询问,那么这 \(M\) 次询问也一定会让我们知道 \(M\) 个值,所以没有超出一个数字平均两次询问机会的限制。
接着将指针前移。如果某个数字已经被确定,就略过他。直到某个未确认数字为止。类似地,我们不断抬高这个数字。先是获得了对他前面某些数字的关系,一直到交互器返回他自己的下标。注意,此时他身后的值都已经确定。那么这里就一定是他和他身后的最小值重合。(注意,他的身后不可能出现已经被确定的比当期数字小的值。因为若比他小的数字被确定,则当前数字也一定会在后面数字被不断抬高时确定。)所以直接记录最小值然后减掉抬高的部分便推出当前数字,同时也推出与他有关系的部分。
等到最后一个空余位置时,可以直接排除出他的值,进一步减少询问次数。
下面考虑上述操作询问次数的合法性。可以发现,对于“ 被关系推出来”的数字,他们消耗的次数都是 \(1\)。而对于需要我们主动确定(被抬升的数),他们也都是在询问至边界时被直接确定。所以上述流程的询问次数是 \(N\) 次,远小于题目给的限制 \(2 * N\)。
代码:
#include <bits/stdc++.h>
using namespace std;
#define N 100010
int n;
int ans[N];
vector <pair<int, pair<int, int> > > G;
int main(){
memset(ans, 0, sizeof(ans));
cin >> n;
int del = 1;
int tmp = 0;
int last = n;
while(666){
printf("? ");
for(int i = 1; i < n; i++){
printf("1 ");
}
printf("%d\n", 1 + del);
fflush(stdout);
last = tmp;
scanf("%d", &tmp);
if(tmp != 0){
G.push_back(make_pair(del, make_pair(n, tmp)));
}
else break;
del++;
}
ans[last] = n;
ans[n] = n - del + 1;
for(auto it : G){
pair<int, int> t = it.second;
ans[t.second] = ans[t.first] + it.first;
}
G.clear();
int minn = 1e9;
for(int i = n; i >= 2; i--){
if(ans[i]){
minn = min(minn, ans[i]);
continue;
}
del = 1;
while(666){
printf("? ");
for(int j = 1; j <= n; j++){
if(j == i) printf("%d ", 1 + del);
else printf("1 ");
}
printf("\n");
fflush(stdout);
scanf("%d", &tmp);
if(tmp == 0) break;
else if(tmp != i) G.push_back(make_pair(del, make_pair(i, tmp)));
else if(tmp == i){
break;
}
del++;
}
if(tmp == 0) ans[i] = n - del + 1;
else if(tmp == i) ans[i] = minn - del;
minn = min(minn, ans[i]);
for(auto it : G){
pair<int, int> t = it.second;
ans[t.second] = ans[t.first] + it.first;
}
G.clear();
}
bool vis[1001];
memset(vis, 0, sizeof(vis));
for(int i = 2; i <= n; i++)
vis[ans[i]] = 1;
for(int i = 1; i <= n; i++)
if(!vis[i]){
ans[1] = i;
break;
}
printf("! ");
for(int i = 1; i <= n; i++)
printf("%d ", ans[i]);
printf("\n");
fflush(stdout);
return 0;
}
标签:tmp,数字,int,题解,询问,妖下,西行,del,ans
From: https://www.cnblogs.com/wondering-world/p/17030653.html