codeforces gym 100917 Interactive Casino 交互题+找种子

46 篇文章 0 订阅
33 篇文章 0 订阅

https://codeforces.com/gym/100917/problem/I

题目大意:交互题,初始你有 160 160 160元,设你当前的钱数为 n n n,每一轮可以赌 k k k元( k < = n k<=n k<=n),此时系统会按照规律产生一个数字,如果该数字二进制有奇数个 1 1 1,那么你可以拿回本金并获得额外的 k k k元,否则你将失去本金。数字的产生规律为: F i = ( 487237 ∗ F i − 1 + 1011807 ) % 2 20 F_{i}=(487237*F_{i-1}+1011807)\%2^{20} Fi=(487237Fi1+1011807)%220;你并不能得到这个数字,判断将由系统完成并返回你的剩余金钱数,当某个时刻你恰好有 200 200 200元的时候,你就获得了胜利。

思路:看见这种题思路还是不够清晰啊,既然能找到必胜态说明一定有循环节。打表找规律,会发现:(1)任取 F 1 = [ 0 , 2 20 − 1 ] F_{1}=[0,2^{20}-1] F1=[0,2201] F i = ( 487237 ∗ F i − 1 + 1011807 ) % 2 20 F_{i}=(487237*F_{i-1}+1011807)\%2^{20} Fi=(487237Fi1+1011807)%220均有一个长度为 2 20 2^{20} 220的循环节;(2)任取 F 1 = [ 0 , 2 20 − 1 ] F_{1}=[0,2^{20}-1] F1=[0,2201],进行 38 38 38轮游戏,我们记录这 38 38 38轮游戏的结果: s i = 1 s_{i}=1 si=1表示第 i i i轮胜利了, s i = 0 s_{i}=0 si=0表示第 i i i轮失败了,那么我们可以得到一个 01 01 01串,计算出其对应的二进制数会发现,这些二进制数没有重复的,也就是说我们进行 38 38 38轮游戏后就可以确定这个序列的种子了(即 F 1 F_{1} F1)。那么思路就很清晰了,每次只赌 1 1 1块钱,赌 38 38 38次后根据胜负情况还原出二进制数,然后依据这个二进制数找种子。找到种子后就知道系统以后生成的数据是什么了~

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;

const int mod=1<<20;
//map<ll,bool> m;
//bool vis[mod+5];

int main()
{
	//38 次即可鉴别
	/*
	for(ll i=0;i<mod;i++)
	{
		ll ret=0;
		ll tmp=i;
		for(ll j=1;j<=38;j++)
		{
			if(__builtin_popcount(tmp)&1)
				++ret;
			ret<<=1;
			tmp=(487237*tmp+1011807)%mod;
		}
		if(m.count(ret))
		{
			cout<<"fail"<<endl;
			break;
		}
		m[ret]=1;
	}
	cout<<"success"<<endl;*/
	//周期为 1<<20
	/*
	for(ll i=0,j=0;i<mod;i++)
	{
		vis[j]=1;
		j=(487237*j+1011807)%mod;
		if(vis[j])
			cout<<j<<endl;
	}
	for(int i=0;i<mod;i++)
		if(!vis[i])
			cout<<"ok"<<endl;
	cout<<"no"<<endl;*/
	int pre,cur;
	ll ret=0;
	cin>>pre;
	for(int i=1;i<=38;i++)
	{
		cout<<1<<endl;
		fflush(stdout);
		cin>>cur;
		ret<<=1;
		if(cur>pre)//赢了
			++ret;
		pre=cur;
	}
	//得到 唯一标识 ret
	ll v1=0,v2=0,tmp=0;
	for(int i=1;i<=38;i++)
	{
		tmp<<=1;
		if(__builtin_popcount(v1)&1)
			++tmp;
		v1=(487237*v1+1011807)%mod;
	}
	if(tmp!=ret) //开始找种子
	{
		ll base=0;
		for(int i=0;i<=36;i++)
			base|=1ll<<i;
		for(int i=1;i<mod;i++)
		{
			tmp&=base; //消去第一位的影响
			tmp<<=1;
			if(__builtin_popcount(v1)&1)
				++tmp;
			if(tmp==ret) //找到种子
			{
				v1=(487237*v1+1011807)%mod;
				break;
			}
			v1=(487237*v1+1011807)%mod;
		}
	}//已经找到种子 下一次输入的数为 v1
	while(1)
	{
		if(__builtin_popcount(v1)&1) //下一次必胜
			cout<<200-cur<<endl;
		else
			cout<<1<<endl;
		fflush(stdout);
		cin>>cur;
		if(cur==-1)
			break;
		v1=(487237*v1+1011807)%mod;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值