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; }
本文作者:yiweixxs
本文链接:https://www.cnblogs.com/yiweixxs/p/18549821
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步