codeforces 895C

来源:互联网 发布:淘宝退款不退货会怎样 编辑:程序博客网 时间:2024/05/19 01:08

(状压dp)
题意:给定一个集合,里面包括n(1n105)个数字a[i](1a[i]70),求出这个集合中有多少子集,使得子集内部所有数字的乘积为平方数。

思路:观察到a[i]的范围比n要小很多,于是我们可以用一个小数组保存:大小为i的数字出现过几次,然后枚举i(1i70)。计数问题很有可能是dp,枚举i时最关键是要想到怎么保存i之前数字的乘积有哪些?这里由于平方数的在质因子分解后,每个质因子的幂一定是偶数,于是想到只保存质因子取二进制最低位后的乘积,然而乘积太大也没法保存。于是观察到70以内的质因子只有不到20个,就可以考虑用状态压缩保存。然后dp方程就很好想了。(最后记得优化一下内存空间)
cnt[i]=0时直接将i1转移到i即可。
cnt[i]0时:

{dp[i][j]=dp[i][j]+dp[i1][j]2cnt[i]1dp[i][jmask[i]]=dp[i][jmask[i]]+dp[i1][j]2cnt[i]1

代码:

#include <iostream>#include <cstdio>#include <vector>#include <cstring>#include <algorithm>#define LL long longusing namespace std;const int maxn = 100050;const LL mod = 1e9 + 7;LL dp[2][1<<19], pw[maxn];int a[maxn], cnt[80], bitmask[80];vector<int> prim;void init() {    // get 2^n    pw[0] = 1;    for(int i=1; i<maxn; i++)        pw[i] = 2LL * pw[i-1] % mod;    prim.clear();    memset(cnt, 0, sizeof(cnt));    memset(bitmask, 0, sizeof(bitmask));    memset(dp, 0, sizeof(dp));    // get prime number    for(int i=2; i<71; i++) {        bool flag = 1;        for(int j=2; j*j<=i; j++)            if(i%j == 0) flag = 0;        if(flag) prim.push_back(i);    }    // get bitmask for each i    for(int i=2; i<71; i++) {        int t = i;        for(int j=0; j<(int)prim.size(); j++) {            int num = 0, div = prim[j];            while(t%div == 0) {                t /= div;                num ++;            }            if(num&1)                bitmask[i] += 1<<j;        }    }}int main() {    //freopen("test.txt","r",stdin);    init();    int n;    scanf("%d",&n);    for(int i=0; i<n; i++) {        scanf("%d",&a[i]);        cnt[a[i]] ++;    }    // solve    int sz = (int)prim.size();    dp[0][0] = 1;    for(int i=1; i<71; i++) {        int mask = bitmask[i], cur = i&1, last = !cur;        for(int j=0; j<(1<<sz); j++) {            if(cnt[i] == 0)                dp[cur][j] = dp[last][j];            else {                dp[cur][j] = (dp[cur][j] + dp[last][j]*pw[cnt[i]-1]%mod) % mod;                dp[cur][j^mask] = (dp[cur][j^mask] + dp[last][j]*pw[cnt[i]-1]%mod) % mod;            }        }        for(int j=0; j<(1<<sz); j++)            dp[last][j] = 0;        //printf("%d : %I64d\n",i,dp[i][0]);    }    printf("%I64d\n",(dp[0][0]-1+mod)%mod);    return 0;}
原创粉丝点击