C. Chocolate Bunny(数学+交互) Codeforces Round #669 (Div. 2)

原题链接: https://codeforces.com/contest/1407/problem/C

在这里插入图片描述
测试样例

inputCopy
3
1
2
1
0
output
? 1 2
? 3 2
? 1 3
? 2 1
! 1 3 2

题意: 这是一个交互问题,有一个1~n的排列数组 p p p,你并不知道它的排列,现在你想通过向系统询问来猜想排列数组。你最多有 2 ∗ n 2*n 2n次的询问机会。询问方式为 ?   i   j ?\ i\ j ? i j,意为询问 p x m o d      p y p_x\mod \ p_y pxmod py的结果。系统会返回给你值。

解题思路: 这道题是一道许久未见的交互题,出的非常好(吹爆cf)。我们首先要知道一个性质,就是我们询问的是 p x m o d      p y p_x\mod \ p_y pxmod py,其中 p x p_x px p y p_y py是不相等的,那么如果我们还询问 p y m o d      p x p_y\mod \ p_x pymod px。有了这两个的值,我们就可以知道其中一个结果了。因为根据取余性质可得:小数mod大数结果还是为小数,而大数mod小数结果一定比小数小。据此解题。 我们判断系统返回的两个结果比较大小不就可以确定原排列中的一个数了吗?由于我们最多有 2 ∗ n 2*n 2n的询问,我们每确定一个就需要2次询问,这完全是够的。(当然,我们去询问时一定要使用两个都未确定的下标。),当然我们最多只能确定 n − 1 n-1 n1个数,因为最大的数无法被确定,所以我们只要询问 2 n − 2 2n-2 2n2次。那么你可能觉得奇怪了,最大的数怎么确定呢?当然可以,我们已经知道了 n − 1 n-1 n1个数。那么最后一个数自然是 n n n,它的下标也自然已知。OK,据此题易解。(注意细节,我们假定一个变量储存未确定的下标,要实时更新,初始为1。然后遍历2~n去询问确定。)

AC代码

/*
*邮箱:unique_powerhouse@qq.com
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include<bits/stdc++.h>	//POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e5;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

int n,p[maxn];
int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>n){
		memset(p,-1,sizeof(p));
		int temp=1;//以temp为固定对比参考
		int p1,p2;//存储系统返回的值。
		rep(i,2,n){
			cout<<"? "<<temp<<" "<<i<<endl;
			cout.flush();//吸收系统返回值。
			cin>>p1;//传递给变量。
			cout<<"? "<<i<<" "<<temp<<endl;
			cout.flush();
			cin>>p2;
			//根据mod可知,小数mod大数结果还是为小数,而大数mod小数结果一定比小数小。据此解题。
			if(p1>p2){
				//说明p[temp] mod p[i]=p[temp];
				p[temp]=p1;//则此时p1已确定,我们要选择未确定的继续进行处理。
				temp=i;
			}
			else{
				p[i]=p2;//说明temp未确定,我们此时无需更换。
			}
		}
		//接下来输出,由于我们只能套出最小的值。最大的值始终是套不出的,所以我们最后剩余的temp就可以填充temp。
		p[temp]=n;
		cout<<"! ";
		rep(i,1,n){
			cout<<p[i]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

posted @   unique_pursuit  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示