1. 程式人生 > >一中模擬賽11.2——Alice的幸運數

一中模擬賽11.2——Alice的幸運數

原題

Solution

引理1:當 n > 6 n>6 時全用 a n d
and
能使最小值為 0 0

1 1 a

n d and 能使 1 1 的個數減半,而 1 1
個數最多 32 32 1 1 ,所以 6 6 個數能使 1 1 的個數為 0 0

引理2: x o r xor 操作能被 a n d and 操作代替

0^0=0;0&~0=0
0^1=1;0&~1=0
1^0=1;1&~0=1
1^1=0;1&~1=0
比較左右兩列,發現 a n d and 操作一定 < = x o r <=xor 操作

引理3:只用 a n d and 一定能得到最優解

n n 個數二進位制展開,可以得到一個大小為 n 64 n*64 的表:
( 011101010101010 ) 2 (011101010101010)_2
( 100110101010100 ) 2 (100110101010100)_2
( 100101111010111 ) 2 (100101111010111)_2
把它豎著看,每列看作一個狀態,不管用什麼運算子,相同狀態的最終結果一定相同
首先假設狀態數大於 2 n 2^n ,因為狀態數 < = 2 n <=2^n 時三種操作都可以做到 0 0
a n d and 可以得到只有一個狀態最終結果為 1 1
要證明的就是 o r or x o r xor a n d and 三種操作混合不能使 2 n 2^n 種不同狀態均為 0 0
假設有一個長度為 k k 01 01 序列,每兩個數之間的操作相同,注意到不管是哪種操作,最終結果都是 0 0 1 1 (且都能取到),於是長度為 n n 的混合序列可以看作若干個這樣的序列(相當於縮點),最終結果是 0 0 1 1 (都能取到)


只用引理1已經能過了這題,但時間複雜度為 O ( n 6 n ) O(n*6^n) ,引理2能優化到 O ( n 4 n ) O(n*4^n) ,引理3能優化到 O ( n 2 n ) O(n*2^n)

Code

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
int T,n,i,s;
ll a[101],ans,t;
int main(){
	for (scanf("%d",&T);T--;){
		scanf("%d",&n);
		for (i=0;i<n;i++) scanf("%llu",&a[i]);
		if (n>6){
			puts("0");
			continue;
		}
		ans=~0;
		for (s=0;s<1<<n;s++,ans=min(ans,t))
			for (i=0,t=~0;i<n;i++) t&=(s&(1<<i)?a[i]:~a[i]);
		printf("%llu\n",ans);
 	}
}