Codeforces 1427E. Xum 題解
阿新 • • 發佈:2020-10-12
題目連結:E. Xum
題目大意:洛谷
題解:咋做法又和官方題解不一樣啊。
一個很好想的思路是考慮每一次將最高位去掉轉換成一個子問題,所以說我們的目標就是構造出最高位的那一個 1,即令 \(high_x\) 為 \(x\) 轉換為二進位制後最高的非 0 位,我們需要找到 \(2^{high_x}\) ,那麼我們可以將 \(x\) 在二進位制意義下向左平移使得平移最小的非 0 位與 \(high_x\) 相等,令這個數為 \(y\) ,然後再將兩個數異或起來,這樣的話我們就可以將 \(high_x\) 處置為 0,令這個數為 \(z\) ,然後再將 \(z+y\) 記為 \(p\),緊跟著將 \(p\oplus x\)
可能上面講得不太好理解,下面舉一個例子(以下所有的數均為二進位制下的數):
x=000111 000111 + 000111 = 001110 001110 + 001110 = 011100 011100 ^ 000111 = 011011 011011 + 001100 = 110111 110111 ^ 000111 = 110000 011100 + 011100 = 111000 110000 ^ 111000 = 001000 011100 ^ 001000 = 010100 001000 + 001000 = 010000 010100 ^ 010000 = 000100 000111 ^ 000100 = 000011
至此,原問題成功地被轉換為了規模更小的子問題。
時間複雜度以及詢問數均為 \(O(\log^2 n)\) 。
程式碼:
#include <cstdio> typedef long long ll; const int Maxn=100000; int x; struct Answer{ int op; ll a,b; }ans[Maxn+5]; int len; bool vis[60]; void work(int x){ if(x==1){ return; } int high; for(int i=19;i>=0;i--){ if((x>>i)&1){ high=i; break; } } ll tmp=x; for(int i=0;i<high;i++){ ans[++len].op=0; ans[len].a=tmp; ans[len].b=tmp; tmp<<=1; } if(!vis[high+1]){ ans[++len].op=1; ans[len].a=tmp; ans[len].b=x; ll now=tmp^x; ans[++len].op=0; ans[len].a=now; ans[len].b=tmp; ll sum=now+tmp; ans[++len].op=1; ans[len].a=sum; ans[len].b=x; sum^=x; ans[++len].op=0; ans[len].a=tmp; ans[len].b=tmp; now=(tmp<<1); ans[++len].op=1; ans[len].a=now; ans[len].b=sum; vis[high+1]=1; } for(int i=high+1;(1ll<<i)<=tmp;i++){ if(!vis[i]){ ans[++len].op=0; ans[len].a=(1ll<<(i-1)); ans[len].b=(1ll<<(i-1)); vis[i]=1; } if((tmp>>i)&1){ ans[++len].op=1; ans[len].a=tmp; ans[len].b=(1ll<<i); tmp^=(1ll<<i); } } vis[high]=1; ans[++len].op=1; ans[len].a=x; ans[len].b=(1ll<<high); x^=(1ll<<high); work(x); } int main(){ scanf("%d",&x); work(x); printf("%d\n",len); for(int i=1;i<=len;i++){ if(ans[i].op==0){ printf("%lld + %lld\n",ans[i].a,ans[i].b); } else{ printf("%lld ^ %lld\n",ans[i].a,ans[i].b); } } return 0; }