這題 *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;
}