Codeforces 895.C Square Subsets

C. Square Subsets
time limit per test
4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Petya was late for the lesson too. The teacher gave him an additional task. For some array a Petya should find the number of different ways to select non-empty subset of elements from it in such a way that their product is equal to a square of some integer.

Two ways are considered different if sets of indexes of elements chosen by these ways are different.

Since the answer can be very large, you should find the answer modulo 109 + 7.

Input

First line contains one integer n (1 ≤ n ≤ 105) — the number of elements in the array.

Second line contains n integers ai (1 ≤ ai ≤ 70) — the elements of the array.

Output

Print one integer — the number of different ways to choose some elements so that their product is a square of a certain integer modulo 109 + 7.

Examples
input
4
1 1 1 1
output
15
input
4
2 2 2 2
output
7
input
5
1 2 4 5 8
output
7
Note

In first sample product of elements chosen by any way is 1 and 1 = 12. So the answer is 2^4 - 1 = 15.

In second sample there are six different ways to choose elements so that their product is 4, and only one way so that their product is 16. So the answer is 6 + 1 = 7.

大致题意:选若干个数使得乘积为完全平方数,问方案数.

 分析:对于完全平方数,它的每个质因子的次数都是偶数.这道题中ai ≤ 70,质因子最多只有19个,可以考虑状压,记一个状态sta,第i位表示第i个质因子的次数为偶数还是奇数,状态的变化可以通过位运算中的异或运算来解决.那么可以设计状态f[i][j]表示前i个数字中状态为j的方案数,每个数字有选或不选两种选择,转移也比较明确.比较遗憾的是n比较大,2^19 * n不足以通过此题.需要换一种状态的表达方式.

           首先状态j,也就是第二维是不能去掉的,关键是第一维该换成什么.2^19已经比较大了,那么第一维也不能很大,能想到的就是表示成1到大小为i的数字中,状态为j的方案数:f[i][j],这样的话不一位一位地去考虑,而是去考虑每个数字出现的次数.转移的时候枚举i和上一次的状态j,那么j能够转移到状态j ^ sta[i]和j,转移到j ^ sta[i]就需要取奇数个i,方案数就是C(n,1) + C(n,3) + ...... = 2^(n-1).j转移到j取偶数个就行了,方案数还是2^(n-1).

           最后的答案就是f[70][0].

           一道非常好的题.设计状态保留必须的,替换其它的,从题目的要求和信息中找到可以替换的状态.也可以从另一个思路来理解这个替换:不需要知道i在哪几位被选,只需要知道i被选了多少次,记录出现的次数并利用组合数学的知识就可以解决了.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;

const int mod = 1e9 + 7;

ll n, a[100010], cnt[80], jie[100010];
int prime[19] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67 };
int f[71][(1 << 19) + 5], stu[80];

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        cnt[a[i]]++;
    }
    jie[0] = 1;
    for (int i = 1; i <= n; i++)
        jie[i] = (jie[i - 1] * 2) % mod;
    for (int i = 1; i <= 70; i++)
    {
        for (int j = 0; j < 19; j++)
        {
            int x = i;
            while (x % prime[j] == 0)
            {
                stu[i] ^= (1 << j);
                x /= prime[j];
            }
        }
    }
    f[0][0] = 1;
    for (int i = 1; i <= 70; i++)
    {
        for (int j = 0; j < (1 << 19); j++)
        {
            if (!cnt[i])
                f[i][j] = f[i - 1][j];
            else
            {
                f[i][j] = (f[i][j] + jie[cnt[i] - 1] * f[i - 1][j] % mod) % mod;
                f[i][j ^ stu[i]] = (f[i][j ^ stu[i]] + jie[cnt[i] - 1] * f[i - 1][j] % mod) % mod;
            }
        }
    }
    f[70][0]--;
    if (f[70][0] < 0)
        f[70][0] += mod;
    cout << f[70][0] << endl;

    return 0;
}

 

 

posted @ 2017-12-16 11:47  zbtrs  阅读(513)  评论(0编辑  收藏  举报