Codeforces 895C Square Subsets:状压dp【组合数结论】
题目链接:http://codeforces.com/problemset/problem/895/C
题意:
给你n个数a[i]。(n <= 10^5, 1 <= a[i] <= 70)
问你有多少非空子集s,使得 ∏(s[i])为完全平方数。
题解:
由于a[i] <= 70,而70以内的质数只有19个,显然可以状压。
由于一个数是完全平方数的条件是:它的每种质因子的指数为偶数
所以先处理出对于每个a[i],它的所有质因子指数的奇偶性f[i]。
对于f[i]的每一位,0表示它的质因子prime[i]的指数为偶,1代表为奇数。
另外由于n比较大,所以dp中状态不能是“当前考虑到a[i]”,而应该是“当前考虑到数字i”。
所以之前要统计一下为数字i的a[j]的个数cnt[i]。(x <= 70)
然后开始状压。
表示状态:
dp[i][state]表示当前考虑到数字i,子集和的质因子指数的奇偶性为state,此时的子集方案数
找出答案:
由于1到70的所有数都会考虑一遍,且最终的子集和的质因子的指数都应该为偶数
另外由于要求是非空子集,最终答案要-1
所以ans = dp[71][0] - 1
如何转移:
对于数字i来说,如果cnt[i] == 0,则直接将所有dp[i][state]复制到dp[i+1][state]即可。
否则分两种情况:数字i选偶数个 or 奇数个。选偶数个i不会影响子集和质因子指数的奇偶性,而奇数个会影响。
(1)偶数个:dp[i+1][state] += dp[i][state] * C(n,m),其中m = 0,2,4,6,8...
(2)奇数个:dp[i+1][state^f[i]] += dp[i][state] * C(n,m),其中m = 1,3,5,7,9...
有一个结论:∑ C(n,{0,2,4,6,8...}) = ∑ C(n,{1,3,5,7,9...}) = 2^(n-1)
之前预处理所有p[i] = 2^i % MOD即可。
边界条件:
dp[1][0] = 1
others = 0
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 100005 5 #define MAX_A 75 6 #define MAX_S 550000 7 #define MOD 1000000007 8 9 using namespace std; 10 11 const int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67}; 12 13 int n; 14 int p[MAX_N]; 15 int f[MAX_A]; 16 int cnt[MAX_A]; 17 int dp[MAX_A][MAX_S]; 18 19 void read() 20 { 21 cin>>n; 22 memset(cnt,0,sizeof(cnt)); 23 int a; 24 for(int i=1;i<=n;i++) 25 { 26 cin>>a; 27 cnt[a]++; 28 } 29 } 30 31 void cal_f() 32 { 33 memset(f,0,sizeof(f)); 34 for(int i=1;i<=70;i++) 35 { 36 int t=i; 37 for(int j=0;j<19;j++) 38 { 39 while(t%prime[j]==0) 40 { 41 f[i]^=(1<<j); 42 t/=prime[j]; 43 } 44 } 45 } 46 } 47 48 void cal_p() 49 { 50 p[0]=1; 51 for(int i=1;i<MAX_N;i++) p[i]=(p[i-1]<<1)%MOD; 52 } 53 54 void cal_dp() 55 { 56 memset(dp,0,sizeof(dp)); 57 dp[1][0]=1; 58 for(int i=1;i<=70;i++) 59 { 60 if(!cnt[i]) 61 { 62 for(int state=0;state<(1<<19);state++) 63 { 64 dp[i+1][state]=dp[i][state]; 65 } 66 } 67 else 68 { 69 for(int state=0;state<(1<<19);state++) 70 { 71 if(dp[i][state]) 72 { 73 dp[i+1][state]=(dp[i+1][state]+(long long)dp[i][state]*p[cnt[i]-1])%MOD; 74 dp[i+1][state^f[i]]=(dp[i+1][state^f[i]]+(long long)dp[i][state]*p[cnt[i]-1])%MOD; 75 } 76 } 77 } 78 } 79 } 80 81 void work() 82 { 83 cal_f(); 84 cal_p(); 85 cal_dp(); 86 cout<<dp[71][0]-1<<endl; 87 } 88 89 int main() 90 { 91 read(); 92 work(); 93 }