题解 CF895C Square Subsets

这伞兵题目花了我两个小时?!

求积是平方数的给定数组子序列个数。

注意到数据范围 \(a_i \le 70\),那么就只有 \(19\) 个质数,可以记录每一个质数。
又因为积只关心所有质因数的个数的奇偶性,所以把它们对 \(2\) 取模,那么就可以愉快地状压了。

但还是有点不对, \(n\)\(10^5\) 呢!然后我就没有办法了。。。
其实对每个数做 dp 也是没有意义的,因为 \({n \choose 0} + {n \choose 2} + {n \choose 4} + \cdots\) 的结果就是 \(2^{n-1}\), 怎么证明这一点呢?可以用归纳法来证明。

首先这个过程就相当于二进制数的 popcount 的奇偶性。

  1. 基本情况:\(n=1\) 的时候显然成立。
  2. \(n = k\) 成立的时候,我们再在前面给它加一个 \(0\)\(1\),保证原来奇偶性的个数都翻了一倍,所以答案就乘上了 \(2\),结论同样成立

那么就真的可以 dp 了!不过要注意预处理二的幂次,因为 \(2^{19} \times 70\) 还是比较大的,里面再来个 \(\log\) 就 T 了。

代码
#include <iostream>
#define int long long
const int P = 1000000007;
int prime[19] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67};
int tr[100], cnt[75][20], sta[75], tot[75], f[1 << 19], g[1 << 19], n, pw[100005];
int Pow(int a, int b) {
    return pw[b];
    int an = 1;
    for ( ; b; b >>= 1, a = a * a % P)
        if (b & 1) an = an * a % P;
    return an;
}
void add(int &a, int b) { a = (a+b) % P; }
signed main() {
    std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
    std::cin >> n;
    for (int i = 0; i < 19; i++) tr[prime[i]] = i+1;
    for (int i = 1, x; i <= n; i++) 
        std::cin >> x, tot[x] ++;
    for (int i = 2; i <= 70; i++) {
        int x = i;
        for (int j = 2; j*j <= x; j++) 
            if (x % j == 0) 
                while (x % j == 0) cnt[i][tr[j]] ++, x /= j;
        if (x != 1) cnt[i][tr[x]] ++;
    }
    for (int i = 1; i <= 70; i++) 
        for (int j = 1; j <= 19; j++) 
            if (cnt[i][j] & 1) sta[i] |= 1 << (j-1);
    pw[0] = 1;
    for (int i = 1; i <= n; i++) pw[i] = pw[i-1] * 2 % P;
    f[0] = 1;
    for (int i = 2; i <= 70; i++) {
        if (!tot[i]) continue;
        for (int S = 0; S < (1 << 19); S++) g[S] = 0;
        for (int S = 0; S < (1 << 19); S++)
            add(g[S ^ sta[i]], Pow(2, tot[i]-1) * f[S] % P), 
            add(g[S], f[S] * Pow(2, tot[i]-1) % P);
        for (int S = 0; S < (1 << 19); S++) f[S] = g[S];
    }
    std::cout << (f[0] * Pow(2, tot[1]) % P - 1 + P) % P;
}


那么我做那么久问题出在哪里呢?

首先是没有想到那个组合数居然可以直接求,也没有想清楚 dp 能不能做就直接写了,写到一半才发现。
其次再去写的时候也没有想清楚,在滚动的时候一些细节上的处理出了问题,写得慢也调得慢。

希望下次能按照 SOP 搞清楚了一切再快速写出代码来。

posted @ 2021-07-29 10:40  Acfboy  阅读(35)  评论(0)    收藏  举报