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 }

 

转载于:https://www.cnblogs.com/doggod/p/8324510.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值