题目大意:
给定数列 a1 , a2 , ... , an
希望找到一个 N = sigma(ai^ki) , (0<=ki<10) ,ki可随自己定为什么
只要保证N的因子和可以表示为 2^t的形式 , 输出t , 找不到就输出 NO
关于梅森素数,有一个重要的定理:“一个数能够写成几个不重复的梅森素数的乘积” 等价于 “这个数的约数和是2的幂次”,但是不能重复,比如说3是梅森素数,9就不满足约数和为2的幂。
还有一个重要内容就是,N的约数和幂次是可以直接由构成它的梅森素数的来源幂次相加而得的。
“一个数能够写成几个不重复的梅森素数的乘积” 等价于 “这个数的约数和是2的幂次” 因为这两个概念是充分必要的
那么就说明在这里要找到一个N表示为几个梅森素数的乘积,那么很明显,这里的ki只能是 0或者1 , 因为只要乘方了,那么必然那个数要么无法用梅森素数的乘积表示
要么乘积中存在重复的梅森素数
这里数字小于 2^31 , 在这个范围内只有 8个梅森素数
一个个枚举跑一遍就行了,最后结合得到答案,可以利用简单的dp背包的思想
1 #include <cstdio> 2 #include <cstring> 3 #include <vector> 4 #include <queue> 5 #include <iostream> 6 #include <set> 7 using namespace std; 8 #define pii pair<int,int> 9 const int p[8] = {2,3,5,7,13,17,19,31}; //mason数的p的位置,表示为2^p-1 10 int mason[9] , len[(1<<9)] , rec[(1<<9)] , tot; 11 bool vis[(1<<9)]; 12 set<int> st; 13 14 int Mason(int b){return (1<<b)-1;} 15 16 void get_mason(){for(int i=0 ; i<8 ; i++)mason[i] = Mason(p[i]);} 17 18 void init() 19 { 20 int all = (1<<8); 21 for(int i=0 ; i<all ; i++){ 22 int t = 0; 23 for(int j=0 ; j<8 ; j++) 24 if(i&(1<<j)) t+=p[j]; 25 len[i] = t; 26 } 27 } 28 29 int ok(int x) 30 { 31 int cur = 0; 32 for(int i=0 ; i<8 ; i++){ 33 if(x%mason[i]==0){ 34 x /= mason[i]; 35 cur|=(1<<i); 36 } 37 } 38 if(x>1) cur = 0; 39 return cur; 40 } 41 42 int main() 43 { 44 // freopen("a.in" , "r" , stdin); 45 get_mason(); 46 init(); 47 int n , a; 48 while(~scanf("%d" , &n)){ 49 st.clear(); 50 for(int i=0 ; i<n ; i++){ 51 scanf("%d" , &a); 52 int state = ok(a); 53 if(state) st.insert(state); 54 } 55 56 memset(vis , 0 , sizeof(vis)); 57 tot = 0; 58 set<int>::iterator it=st.begin(); 59 for( ; it!=st.end() ; it++) rec[tot++] = *it; 60 int all = (1<<8); 61 vis[0] = true; 62 for(int i=0 ; i<tot ; i++){ 63 for(int j=all-1 ; j>=rec[i] ; j--) 64 if((rec[i]&j) == rec[i]) vis[j] = vis[j-rec[i]] | vis[j]; 65 } 66 int ret = 0; 67 for(int j=0 ; j<all ; j++) 68 if(vis[j]) ret = max(ret , len[j]); 69 if(ret) printf("%d " , ret); 70 else puts("NO"); 71 } 72 return 0; 73 }