「题解」「CF1103B」Game with modulo

简易中文题目

猜一个数字 \(a\),而你可以向机器提问一对 \((x,y)\) ,如果 \(x\bmod a\ge y \bmod a\) 机器返回字符串 x,反之返回字符串 y
询问不能超过 \(60\) 次,请你猜出 \(a\)

解析

一道十分巧妙的数学题+交互题(人生第二道交互题)。

考虑我们询问的数对是 \((x,2x)\) ,那么就有:

  1. \(a>2x\) ,那么 \(x\bmod a < 2x\bmod a\)
  2. \(x< a \le 2x\) ,那么 \(2x\bmod a=2x-a\) ,又因为 \(x< a\) 那么 \(x\bmod a > 2x\bmod a\)
  3. \(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;
}
posted @ 2020-02-02 17:52  Arextre  阅读(154)  评论(1编辑  收藏  举报