P11490 Staring Contest 题解

11490DX Re: Master Lv.15

 

被题目编号吸引进来的一位。


首先我们可以考虑 个整数:假设其为 。这三个整数因为在题目中是两两不同的,所以根据它们的相对大小关系,赋值为 ,共有 种情况,如下图:

可以发现,我们可以通过查询这 个数的两两之间的 值,就可以找到那个最小值的位置和最小值的值。

算法 1: 做法

所以可以根据这个来从左到右每 个数暴力枚举,找到最小值就把它给踢掉,然后再新插入一个数作为 继续暴力。每次暴力询问 次,一共就是约 次,明显过大,通过不了此题。

算法 2: 做法

我们发现询问完了把一个数踢掉了之后,剩下的那两个数就会平移到前面作为 ,而这两个数的 值是之前问过的,就可以将其代入而无需询问。这样就可以做到新插进来一个数而只需询问 次的更优的算法。但是通过不了 的点,算法仍需优化。


以下定义前缀次大值个数为

算法 3: 做法

我们再看上面的那个表,其实问 次都多余了。把表的最后一项 项删除,会发现并没有什么影响。因为关系就那么几种,所以只需要问两次,即 的值即可得到最小值的位置和数值。所以当新的一个数插进来时,只需问 次就可以求得最小值和最小值的位置。当然这对最小值在 时是对的,而当最小值在 时情况比较特殊。因为下一次询问还需要当前 的前提。

分析这种情况:根据之前提到的,每次操作都是把最小值剔除然后向前平移到 ,我们可以得到新 一定是新 加进来之前的前缀最大值和前缀次大值。当然顺序还不知道。于是当 作为最小值的时候,要么就是前缀最大值更新了,要么就是前缀次大值更新了。我们发现前缀次大值更新是完全包含前缀最大值更新这种情况的。因为当前缀最大值更新时,旧的前缀最大值就会作为新的前缀次大值。所以就可以将其当作前缀次大值更新。综上,当 作为最小值时,一定是前缀次大值更新了,由于在这种情况中询问次数要加上 ,所以就相当于总询问次数加上了总的前缀次大值个数。

算法 4:期望询问次数

注:这里的 为调和级数,即

因为如果使用算法 ,某些良心(?)的出题人就会卡你 corner,直接造一个前缀次大值个数极大的 corner case,就会通过不了。那么我们只能把序列 random_shuffle 一下,竟然通过了此题!这里证明一下询问次数为什么在 random_shuffle 之后就是对的。

因为序列重新打乱过了,所以这里的复杂度就会是期望询问次数。而打乱过后的期望询问次数依然是 。之前说到前缀次大值个数可以通过两种情况更新:

  • 新的前缀最大值更新了旧的前缀最大值,而旧的前缀最大值就成为了新的前缀次大值。

    这种情况就要求期望前缀最大值的个数。我们首先可以把序列离散化一下,就变成了一个 的排列。

    容易如果数 成为了前缀最大值,那么比 大的数就一定在 的后面,所以留给前面的空间至少也必须要有

    这种情况下 对答案的贡献就是 。所以期望前缀最大值的个数就是

    注:

  • 新的数比前缀最大值小,但比前缀次大值大。这种情况也可以用类似上面的证明求,这种数的期望也不会超过

所以这种期望询问次数 ,可以通过此题。

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//P11490dx.cpp
#include <bits/stdc++.h>
using namespace std;
int pos[4], val[4], ans[2009], idx[2009];
int main(){
int n;
cin>>n;
cout<<"? 1 2\n";
fflush(stdout);
cin>>val[1];
pos[1] = 1, pos[2] = 2;
for(int i=1;i<=n;i++){
idx[i] = i;
}
random_shuffle(idx+3, idx+1+n);
for(int i=3;i<=n;i++){
pos[3] = idx[i];
cout<<"? "<<pos[2]<<' '<<pos[3]<<'\n';
fflush(stdout);
cin>>val[2];
if(val[1] == val[2]){
cout<<"? "<<pos[1]<<' '<<pos[3]<<'\n';
fflush(stdout);
cin>>val[3];
ans[pos[2]] = val[1];
pos[2] = pos[3];
val[1] = val[3];
}else if(val[1] < val[2]){
ans[pos[1]] = val[1];
pos[1] = pos[2], pos[2] = pos[3];
val[1] = val[2];
}else{
ans[pos[3]] = val[2];
}
}
ans[pos[1]] = ans[pos[2]] = val[1];
cout<<"!";
for(int i=1;i<=n;i++){
cout<<' '<<ans[i];
}
return 0;
}
  • Title: P11490 Staring Contest 题解
  • Author: 11490DX
  • Created at : 2025-01-09 09:15:32
  • Updated at : 2025-02-14 13:07:39
  • Link: https://11490dx.net/2025/01/09/20250109-P1/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments