1. 程式人生 > 實用技巧 >Codeforces 1427E. Xum 題解

Codeforces 1427E. Xum 題解

題目連結: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\)

得到 \(q\) ,最後在將 \(y+y\) 得到 \(h\) ,然後將 \(q\oplus h\) 得到 \(2^{high_x+1}\) ,然後在利用這一個數將 \(y\) 中在 \(>high_x\) 的位全部異或掉,這樣的話我們就得到了 \(2^{high_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;
}