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

mac2022-06-30  21

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; }
最新回复(0)