题解:P11490 [BalticOI 2023] Staring Contest
前言
第一次做无题解的灰题,有点激动。
思路分析
首先从小数据开始思考。
当 时,可以询问 ,然后返回 。
当 时,可以询问 ,然后分讨一下:
-
当 时,可以确定 ;
-
当 时,可以确定 ;
-
当 时,可以确定 。
不难发现, 的做法具有很强的拓展性。
具体而言,可以每次维护一个待确定集合,然后用两次询问得到一个值,直到未确定的值的数量为 为止。总询问次数为 。实现这个算法有 分。
在上面的算法的基础上,我们可以进行优化,具体而言,我们很多次询问是冗余的,考虑每次处理 三个数时,询问 ,通过三次询问可以确定两个数,总询问次数小于 。实现这个算法有 分。
其实上面的算法已经很接近正解了。
考虑进一步优化。注意到每次询问 时,得到的答案只有为 ,需要再次询问,否则可以利用之前的询问继续递归处理。
也就是,每次已知 ,希望利用一次询问确定一个值 ,分类讨论:
下文为了方便,考虑令 。
-
当 时,可以确定 ,递归处理 ;
-
当 时,可以确定 ,递归处理 ;
-
当 时,可以确定 ,选择一个新的 处理。
不难发现,1 和 3 操作都是一次询问确定一个值,而 2 操作,本质上不会劣于我们之前所说的询问次数为 的算法。
所以总体的询问次数是玄学,提交发现被卡了。
但是考虑每次随机选择 ,就过了。
不会证明询问次数的正确性,不知道有没有期望复杂度保证。
代码实现
#include<bits/stdc++.h>
using namespace std;
const int inf=-(1ll<<31);
int n,a,b,c,t1,t2,t3,ans[3005],id[3005],val[3005];
queue<int> v;
mt19937 rnd(time(0));
int ask(int x,int y){
int ans;
cout<<"? "<<id[x]<<' '<<id[y]<<endl;
cin>>ans;
return ans;
}
void solve(int a,int b,int t1){
while(!v.empty()){
c=v.front();
v.pop();
t2=ask(a,c);
if(t1==t2) return ans[id[a]]=t1,solve(b,c,ask(b,c));
else if(t2>t1) return ans[id[b]]=t1,solve(a,c,t2);
else ans[id[c]]=t2;
}
ans[id[a]]=ans[id[b]]=t1;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
id[i]=i;
}
shuffle(id+1,id+1+n,rnd);
for(int i=3;i<=n;i++){
v.push(i);
}
solve(1,2,ask(1,2));
cout<<"! ";
for(int i=1;i<=n;i++){
cout<<ans[i]<<' ';
}
cout<<endl;
return 0;
}
本文作者:Kenma
本文链接:https://www.cnblogs.com/Kenma/p/18689778
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步