Codeforces 895C Square Subsets:狀壓dp【組合數結論】

Leohh發表於2018-02-24

題目連結: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 }

 

相關文章