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;
}