「题解」「CF1103B」Game with modulo
简易中文题目
猜一个数字 \(a\),而你可以向机器提问一对 \((x,y)\) ,如果 \(x\bmod a\ge y \bmod a\) 机器返回字符串 x
,反之返回字符串 y
。
询问不能超过 \(60\) 次,请你猜出 \(a\) 。
解析
一道十分巧妙的数学题+交互题(人生第二道交互题)。
考虑我们询问的数对是 \((x,2x)\) ,那么就有:
- \(a>2x\) ,那么 \(x\bmod a < 2x\bmod a\)
- \(x< a \le 2x\) ,那么 \(2x\bmod a=2x-a\) ,又因为 \(x< a\) 那么 \(x\bmod a > 2x\bmod a\)
- \(a\le x\) ,无法分析。
发现第三种情况无法分析,怎么办?
尽可能从小开始枚举 \(x\) ,这样就尽可能避免 \(a\le x\) 的情况发生。
考虑从 \(x=1,2x=2\) 开始枚举,如果过程返回 y
,那么我们枚举小了,继续扩大 \(x\),反之说明 \(x< a\le 2x\) ,在这个区间之内做二分即可。
但是我们怎么枚举 \(x\) 呢?其实有多种方法,这里我推荐使用倍增 因为它的时间复杂度好算 。
即 \(x\) 按照 \(1,2,4,8,16\ldots\) 枚举,这样可以在 \(\log a\) 的时间内求出 \(a\) 的大致范围。
但是由于我们的 \(x\) 是从 \(1\) 开始枚举的,而 \(1\le x\) 属于第三种情况,我们无法处理,所以需要特判 \(1\) 。
另外,如果是使用 printf()
的大佬需要在每一次输出之后用 fflush(stdout)
清空一下输出缓冲区的东西,不然会出现玄学错误。
剩下的就是代码实现了。
代码
切莫直接 copy
,他好,你也好[手动滑稽]。
#include<cstdio>
#include<cstring>
#define rep(i,__l,__r) for(signed i=__l,i##_end_=__r;i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=__l,i##_end_=__r;i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define uint unsigned int
#define pii pair< int,int >
#define Endl putchar('\n')
// #define FILEOI
#define int long long
// #define int unsigned
#ifdef FILEOI
# define MAXBUFFERSIZE 500000
inline char fgetc(){
static char buf[MAXBUFFERSIZE+5],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXBUFFERSIZE,stdin),p1==p2)?EOF:*p1++;
}
# undef MAXBUFFERSIZE
# define cg (c=fgetc())
#else
# define cg (c=getchar())
#endif
template<class T>inline void qread(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
inline int qread(){
int x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
template<class T>void fwrit(const T x){
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int MAXA=1e9;
char s[105],res[105];
int l,r,mid;
inline bool Compare(const char a[],const char b[]){
int la=strlen(a),lb=strlen(b);
if(la^lb)return false;
rep(i,0,la-1)if(a[i]!=b[i])return false;
return true;
}
inline bool Ask(const int x,const int y){
printf("? %lld %lld\n",x,y);fflush(stdout);
scanf("%s",res);
return res[0]=='x';
}
signed main(){
#ifdef FILEOI
freopen("file.in","r",stdin);
freopen("file.out","w",stdout);
#endif
while(233333){
scanf("%s",s);if(Compare(s,"end"))break;
//特判 1
if(Ask(0,1)){
printf("! 1\n");fflush(stdout);
continue;
}
for(int i=1;i<=MAXA;i<<=1)if(Ask(i,Min(i<<1,MAXA<<1))){
l=i,r=Min(i<<1,MAXA<<1);
break;
}
while(l+1<r){
mid=(l+r)>>1;
if(Ask(l,mid))r=mid;
else l=mid;
}
printf("! %lld\n",r);fflush(stdout);
}
return 0;
}