CF2036G Library of Magic

Problem

给出1~n每个数2个,共2n个,然后拿走3个不相等的数,可以进行最多150次询问,可以得到值为l-r的所有数的异或和,请你最后给出这3个数。其中\(3\le n\le10^{18}\)

Solve

不建议做法:

分治,不断给1~n区间分块
原因:需要进行的询问在不优化的情况下能达到200左右,需要不断找地方优化,且存在一些毒瘤情况

正解

设答案为a,b,c且有序
可以尝试进行二分,不断询问1~x的异或和来得到a和c,最后可以询问a至c之间来得到b
可是不难发现这种方案会被形如\(a\oplus b\oplus c=0\)的数据hack掉,怎么办呢?

重点

\(base(x)\)为x的二进制位数。如果\(a\oplus b\oplus c=0\),那么有:$$base(a)<base(b)=base(c)$$
证明:
如果三个bit异或结果为0,那么有偶数个bit为1
因为最高位不为0,所以最高位至少有2个bit为1,另一个最小的为0
又因为a<b<c,所以base(a)<base(b)=base(c)

有了这个,我们可以发现\(a<2^p\le b\)(因为一个位数多,一个位数少)
我们可以枚举这个P,不断询问\(1\sim 2^p-1\),直到结果不为0,此时的结果就是a
这样二分的范围就有单调性了,为\(a+1\sim n\)

Code

#include<bits/stdc++.h>
using namespace std;
long long T,n;
long long gx(long long l,long long r){
    cout<<"xor "<<l<<" "<<r<<endl;
    long long tmp=0;
    cin>>tmp;
    return tmp;
}
int main(){
    cin>>T;
    int test=0;
    while(T--){
        test++;
        cin>>n;
        long long tmp=gx(1,n);
        if(tmp){
            long long l=1,r=n,ans;
            while(l<r){
                long long mid=(l+r)/2;
                if(gx(1,mid)){
                    r=mid;
                }else{
                    l=mid+1;
                }
            }
            ans=l;
            l=1,r=n;
            while(l<r){
                long long mid=(l+r)/2;
                if(gx(1,mid)==tmp){
                    r=mid;
                }else{
                    l=mid+1;
                }
            }
            long long tmp1=gx(ans,l)^ans^l;
            cout<<"ans "<<ans<<" "<<tmp1<<" "<<l<<endl;
        }else{
            long long sum=1,ans1=0,ans2=0,ans3=0,l,r=n;
            for(int i=0;i<=__lg(n)+1;i++){
                sum*=2;
                ans1=gx(1,sum-1);
                if(ans1){
                    break;
                }
            }
            l=ans1+1;
            while(l<r){
                long long mid=(l+r)/2;
                if(gx(1,mid)==tmp){
                    r=mid;
                }else{
                    l=mid+1;
                }
            }
            ans3=l;
            ans2=gx(ans1,ans3)^ans1^ans3;
            cout<<"ans "<<ans1<<" "<<ans2<<" "<<ans3<<endl;
        }
    }
    return 0;
}
posted @ 2024-11-16 21:32  一位XXS  阅读(23)  评论(0)    收藏  举报