Codeforces 843B AIM Tech Round 4 (Div. 2) D:随机拼脸(注意rand()的范围)

这道题启示我们。。有时候交互题也是可以做的。。。有时候真的不是你脸太黑的问题。。要对自己的脸有自信


题意:给出一个singly linked list,就是单向链表,size最多5W,是那种每个节点只知道自己的值以及下一个节点指针的链表。这个list已经升序排序好了,输入数字个数n,链表首地址st,以及询问x,要求你找到lower_bound(x)的值,就是第一个不小于x的值。链表对你不可见,但是你可以询问最多2000次。每次询问一个位置index,然后系统会告知你index位置的数据是value,并且下一个节点在nxt位置。

题解: 显然2000次询问踩5W个数字,别指望有什么算法可以保证得到正确解。别无他法,只好随机化。稍微动动脑子就知道,大量随机数,比如几百次,随机数的分布基本上是比较均匀的。而且题目中链表也是“随机”占位置的,可以认为基本上均匀出随机数。

当然这并没有什么卵用。具体思路是:先随机500次(也可以更多,注意不要重复。),然后我们取这500次随机询问中,value比x小的中,最大的一个。就是尽可能找到一个比较大的 小于x的数字。然后从这个位置开始顺序一个一个的询问,直到发现一个不小于x的输出,或者找到了nxt=-1就说明不存在比x大的数字。

概率证明:假设我们先随机500次然后走1500步找不到答案。假设正确答案在Y位置。那就说明我们前面500次随机数没有一个落在 到Y-1500 到 Y之间。那么随机一次不落在这个区间的概率是1-1500/50000=0.97,那么随机500次一次都不落在这个区间的概率是0.97^500=2e-7(千万分之二),也就是说如果有一千万组测试点,答案错误次数的期望值是2次。因此。。WA了只能说明脸太黑。。


Warning:rand()出随机数的范围是32767。本题千万不能用rand()出随机数字,因为32767-50000这么大的区间随机不到,这个跨度有一万多呢,答案要是在这里边直接GG。解决办法:rand()*rand()即可。


Code:

#include<bits/stdc++.h>
using namespace std;
const int MAX = 5e4+100;
const int MAGIC = 1000;
bool used[MAX];
int bestvalue;
int bestindex;
#define random(a,b) rand()*rand()%(b-a+1)+a
const int INF = 0x3f3f3f3f;
int value,nxt,x,n,st;
int main(){
	srand(time(NULL));
	scanf("%d%d%d",&n,&st,&x);
	cout<<"? "<<st<<endl;
	scanf("%d%d",&bestvalue,&nxt);
	bestindex = st;
	if (bestvalue>=x){
		cout<<"! "<<bestvalue<<endl;
		return 0;
	}
	if (n<1900){
		while (true){
			cout<<"? "<<bestindex<<endl;
			scanf("%d%d",&value,&bestindex);
			if (value>=x){
				cout<<"! "<<value<<endl;
				return 0;
			}
			if (bestindex==-1){
				cout<<"! -1\n"<<endl;
				return 0;
			}
		}
	}
	for (int i=0;i<MAGIC;i++){
		int tt=random(1,n);
		while (used[tt])tt = random(1,n);
		used[tt] = true;
		cout<<"? "<<tt<<endl;
		scanf("%d%d",&value,&nxt);
		if (value==x){
			cout<<"! "<<value<<endl;
			return 0;
		}
		if (value<x){
			if (value>bestvalue){
				bestvalue = value;
				bestindex = tt;
			}
		}
	}
	for (int i=0;i<=795;i++){
		cout<<"? "<<bestindex<<endl;
		scanf("%d%d",&value,&nxt);
		if (value>=x){
			cout<<"! "<<value<<endl;
			return 0;
		}
		if (nxt==-1){
			cout<<"! -1"<<endl;
			return 0;
		}
		bestindex = nxt;
	}
	cout<<"你脸黑"<<endl;
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值