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;
}
标签:tmp,Magic,询问,long,异或,Library,oplus,bit,CF2036G
From: https://www.cnblogs.com/yiweixxs/p/18549821