「杂题乱刷2」CF2036G
这题 *2400 纯唐吧,感觉 *1800 差不多。
题目链接
CF2036G Library of Magic(*2400)
解题思路
注:\(\oplus\) 表示异或运算。
首先我们想一个通解,就是先二分出第一个数和第三个数,然后第二个数就是所有数的异或和异或上这两个数,操作次数为 \(2 \times \log n + 1\),可以通过。
但是有个情况特别难受,就是可能会出现中间有数字但是询问结果为 \(0\) 的情况。
考虑何时会出现这种情况。
由于题目里保证删的数字不同,因此显然查询结果为 \(0\) 时这个区间中只含 \(0\) 或 \(3\) 个数字。
于是我们先判断是否有 \(ans1 \oplus ans2 \oplus ans3 = 0\),若不是,我们可以直接套用上述二分的做法。
否则,由于这三个数的异或和为 \(0\),因此我们可以找到含有最高二进制位的两个数字,那么此时我们可以二分出这两个数字中的其中一个数字,另一个数字可以通过这两个数的异或和异或查询出来的数确定,剩下一个数可以通过所有数的异或和异或这两个数字的异或和确定。
操作次数 \(2 \times \log n + 1\),可以通过。
参考代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define forl(i,a,b) for(re ll (i)=(a);i<=(b);(i)++)
#define forr(i,a,b) for(re ll (i)=(a);i>=(b);(i)--)
#define QwQ return 0;
ll _t_;
void _clear(){}
ll n;
ll ans[10];
ll get(ll x,ll y)
{
ll S=0;
forl(i,1,3)
if(x<=ans[i] && ans[i]<=y)
S^=ans[i];
return S;
}
ll ask(ll x,ll y)
{
if(x>n)
return 0;
Min(y,n);
cout<<"xor "<<x<<' '<<y<<endl;
ll z;
cin>>z;
// z=get(x,y);
return z;
}
void print(ll x,ll y,ll z){
cout<<"ans "<<x<<' '<<y<<' '<<z<<endl;
}
ll pw(ll x){
return 1ll<<x;
}
void solve()
{
_clear();
cin>>n;
ans[1]=1;
ans[2]=2;
ans[3]=3;
if(n==3)
{
print(1,2,3);
return ;
}
if(ask(1,n)==0)
{
// cout<<"AWaDa!\n";
forr(i,61,0)
{
ll num=ask(pw(i),pw(i+1)-1);
if(num!=0 && !(num&pw(i)))
{
// cout<<i<<endl;
ll L=pw(i),R=pw(i+1)-1;
while(L<R)
{
ll Mid=(L+R)/2;
ll num=ask(pw(i),Mid);
if(num==0)
L=Mid+1;
else if(ask(pw(i),Mid)&pw(i))
R=Mid;
else
R=Mid;
}
ll _1=L,_2=num^L,_3=_1^_2;
print(_1,_2,_3);
return ;
}
}
exit(-1);
}
else
{
ll all=ask(1,n);
ll _1=0,_2=0,_3=0;
ll L=1,R=n;
while(L<R)
{
ll Mid=(L+R)/2;
if(ask(1,Mid)==0)
L=Mid+1;
else
R=Mid;
}
_1=L;
L=1,R=n;
while(L<R)
{
ll Mid=(L+R+1)/2;
if(ask(Mid,n)==0)
R=Mid-1;
else
L=Mid;
}
_3=L;
_2=all^_1^_3;
print(_1,_2,_3);
}
}
int main()
{
_t_=1;
cin>>_t_;
while(_t_--)
solve();
QwQ;
}