• 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;
    }
  • 相关阅读:
    6月29日训练 题解
    python笔记
    零样本和少样本学习
    基于图的 Affinity Propagation 聚类计算公式详解和代码示例
    pandas.read_csv() 处理 CSV 文件的 6 个有用参数
    100+数据科学面试问题和答案总结 基础知识和数据分析
    基于趋势和季节性的时间序列预测
    Github Copilot 值得购买吗?使用GitHub Copilot进行快速EDA的示例
    ArgMiner:一个用于对论点挖掘数据集进行处理、增强、训练和推理的 PyTorch 的包
    一个简单但是能上分的特征标准化方法
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8046194.html
Copyright © 2020-2023  润新知