CF1088D Ehab and another another xor problem 做题记录

交互题,系统有两个整数 (a,b)(a,b),你每次可以询问一组整数 (c,d)(c,d),系统会回答:

  • 11 如果 ac>bda\oplus c>b\oplus d
  • 00 如果 ac=bda\oplus c=b\oplus d
  • 1-1 如果 ac<bda\oplus c<b\oplus d

其中操作 aba\oplus b 表示 aabb 按位异或。

你需要在询问不超过 6262 次之后输出 (a,b)(a,b) 的值,保证 0a,b<2300\le a, b < 2^{30}

首先显然可以询问一次得到 a,ba,b 的大小关系 ff,接下来分类讨论:

  • f=0f=0,那么 aabb 相等,从高位向低位依次枚举,显然第 ii 位为 11 当且仅当 a>(b2i)a>(b\oplus 2^i)。这样就可以用 3030 次询问确定 aabb

  • f=0f\not=0,那么从高位向低位依次枚举。枚举到第 ii 位时设当前(确定了前面的位的)答案为 AABBaAa\oplus AbBb\oplus B 的大小关系为 FF

    显然刚开始 A=0,B=0,F=fA=0,B=0,F=f

    枚举到第 ii 位时,询问 (aA2i)(a\oplus A\oplus 2^i)(bB2i)(b\oplus B\oplus 2^i) 的大小关系,即询问两个数第 ii 位和更低位都异或上 2i2^i 的大小关系。设询问的结果为 EE,分类讨论:

    • E=FE=F,那么 aabb 的第 ii 位相等,这时若 (aA)>(bB2i)(a\oplus A)>(b\oplus B\oplus 2^i) 那么 aabb 的第 ii 位为 11,因为 (aA)(a\oplus A)(bB)(b\oplus B) 的最高位都是第 ii 位,这一位均为 11 那么异或 2i2^i 相当于消去最高位,显然会变得比另一个小;
    • E=FE\not=F,那么 aabb 的第 ii 位不相等,这时若 F=1F=1 那么 aa 的第 ii 位为 11,否则 bb 的第 ii 位为 11。更新完 AABB 之后再问一次 (aA)(a\oplus A)(bB)(b\oplus B) 的大小关系来更新 FF,注意若此时 FF 更新成 00 那么剩下的位就要按照 f=0f=0 情况的方法处理。

    代码如下:

    // Problem: CF1088D Ehab and another another xor problem
    // Contest: Luogu
    // URL: https://www.luogu.com.cn/problem/CF1088D
    // Memory Limit: 250 MB
    // Time Limit: 1000 ms
    // 
    // Powered by CP Editor (https://cpeditor.org)
    
    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    const int N=29;
    
    int a,b;
    
    inline int que(int c,int d)
    {
    	printf("? %d %d\n",c,d);
    	fflush(stdout);
    	int x;
    	scanf("%d",&x);
    	return x;
    }
    
    int main()
    {
    	int f=que(0,0);
    	int srt=N;
    	if(f!=0)
    	{
    		for(int i=N;i>=0;i--)
    		{
    			int val=1<<i;
    			int pre=que(a^val,b^val);
    			if(pre!=f)
    			{
    				if(f==1) a|=val;
    				else b|=val;
    				f=que(a,b);
    				if(f==0)
    				{
    					srt=i-1;
    					break;
    				}
    			}
    			else if((f==1&&que(a^val,b)==-1)||(f==-1&&que(a,b^val)==1)) a|=val,b|=val;
    		}
    	}
    	for(int i=srt;i>=0;i--)
    	{
    		int val=1<<i;
    		if(que(a,b^val)==1) a|=val,b|=val;
    	}
    	printf("! %d %d\n",a,b);
    	fflush(stdout);
    	return 0;
    }