Codeforces 895C - Square Subsets 状压DP
题意:
给了n个数,要求有几个子集使子集中元素的和为一个数的平方。
题解:
因为每个数都可以分解为质数的乘积,所有的数都小于70,所以在小于70的数中一共只有19个质数。可以使用状压DP,每一位上0表示这个质数的个数为偶数个,1表示为奇数个。这样的话,如果某个数为一个数的平方的话,那么每个质数个数都是偶数,用0可以表示。从1-70开始状压DP,先存下每个数出现多少次,然后dp转移,dp转移时分别计算某个数出现奇数次还是偶数次的方案数.
这里有一个公式:C(n,0)+C(n,2)+……=C(n,1)+C(n,3)+……=2^(n-1);
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAX_N = 1e5+7; 4 const int MOD = 1e9+7; 5 int vec[75],tran[75],sum[MAX_N]; 6 int dp[75][(1<<19)+4]; 7 int prime[20] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67 }; 8 int main() 9 { 10 int N,M,T; 11 while(cin>>N) 12 { 13 memset(vec,0,sizeof(vec)); 14 memset(tran,0,sizeof(tran)); 15 memset(sum,0,sizeof(sum)); 16 memset(dp,0,sizeof(dp)); 17 for(int i=0;i<N;i++) 18 { 19 int temp; 20 scanf("%d",&temp); 21 vec[temp] ++; 22 } 23 for(int i=1;i<=70;i++) 24 { 25 int t = i; 26 for(int j=0;j<19;j++) 27 { 28 while(t%prime[j] == 0) 29 { 30 tran[i] ^= (1<<j); 31 t /= prime[j]; 32 } 33 } 34 } 35 sum[0] = 1; 36 for(int i=1;i<=N;i++) 37 { 38 sum[i] = (sum[i-1]*2)%MOD; 39 } 40 dp[0][0] = 1; 41 for(int i=1;i<=70;i++) 42 { 43 44 if(vec[i] == 0) 45 { 46 for(int j=0;j<(1<<19);j++) dp[i][j] = dp[i-1][j]; 47 } 48 else 49 { 50 for(int j=0;j<(1<<19);j++) 51 { 52 //奇数 53 dp[i][j^tran[i]] = (dp[i][j^tran[i]] + (long long )dp[i-1][j]*sum[vec[i]-1])%MOD; 54 //偶数 55 dp[i][j] = (dp[i][j] + (long long )dp[i-1][j]*sum[vec[i]-1])%MOD; 56 } 57 } 58 } 59 cout<<(dp[70][0] - 1)%MOD<<endl; 60 } 61 return 0; 62 }