Codeforces Round #448 (Div. 2)C. Square Subsets

题目连接:(C. Square Subsets)[http://codeforces.com/contest/895/problem/C]

题意:给N个范围在1~70的数找出有多少种非空子集之积是一个平方数。

题解:我们可以发现170之间只有19个质数,编号(119)。要使的某个数是个平方数肯定每个质数出现偶数次。我们可以用状压dp

先预处理1~70每个质因子出现的次数偶数为0奇数为1,比如i这个数第一个质数因子出现奇数次s[i]的最后一位为1,

状态转移方程见代码;

#include<bits/stdc++.h>
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#define pb push_back
#define ll long long
#define PI 3.14159265
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define eps 1e-7
const int mod=1e9+7;
const int maxn=1e5+5;
using namespace std;
int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};
int dp[71][(1<<19)+5];
int s[100];
int c[maxn];
int cnt[75],n;
void init()
{
    for(int i=1;i<=70;i++)
    {
            int x=i;
            for(int j=0;j<19;j++)
            {
               while(x%prime[j]==0)
               {
                   s[i]^=(1<<j);
                    x/=prime[j];
               }
            }
    }
    c[0]=1;
    for(int i=1;i<=n;i++)
    {
        c[i]=((ll)c[i-1]*2)%mod;
    }
}
int main()
{
     std::ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    init();
    int a;
    for(int i=0;i<n;i++)
    {
        cin>>a;cnt[a]++;
    }
    dp[0][0]=1;
    for(int i=1;i<=70;i++)
    {
        if(!cnt[i])
        {
            for(int j=0;j<(1<<19);j++)
            {
                dp[i][j]=dp[i-1][j];
            }
        }
        else
        {
            for(int j=0;j<(1<<19);j++)
            {
                dp[i][j]=(dp[i][j]+(ll)dp[i-1][j^s[i]]*c[cnt[i]-1])%mod;//取奇数个
                dp[i][j]=(dp[i][j]+(ll)dp[i-1][j]*c[cnt[i]-1])%mod;//取偶数个
            }
        }
    }
    cout<<dp[70][0]-1<<endl;//减去全不选的情况
    return 0;
}

posted @ 2017-12-12 18:37  lhclqslove  阅读(99)  评论(0编辑  收藏  举报