codeforces895C Square Subsets

来源:互联网 发布:php $_file 编辑:程序博客网 时间:2024/06/06 08:40

什么,这是状压DP???
是在下输了…输的心服口服…
ABC都是好题啊…


前排提醒,由于latex没有^这个符号,所以用代替
首先看看数据范围,70这个玄学范围,emmm…
理性分析查找题解一波,我们想起来要的是完全平方数,那么就可以搞一些大新闻:
dp[i][j]表示前i个数达到状态j的情况有多少种,这里的状态j表示某一个因子有奇数个还是偶数个,奇数为1偶数为0,显然j=0,所以我们其实是统计dp[70][0]
由于n70,而在70个数内一共有19个质数,这无疑证明了我们的思路是可行的。

首先我们需要知道一条结论:
n{i|i}Cin=n{i|i}Cin=2n1(1)
那么我们可以预处理出来s[i]表示70个数所代表的状态,cnt[i]表示某个数出现次数,powmod[i]表示2i%MOD的值,然后思考转移。

对于某个数p,其状态为s[p]
如果cnt[p]=0,那么dp[p][j]=dp[p1][j]
否则有两种选择,一种是选择奇数个p,即

dp[p][js[p]]+=powmod[cnt[p]1]×dp[p1][js[p]]

这是因为在这种情况下,异或奇数次相当于异或一次,所以这就是求解n个数里选奇数个的情况总和,带入公式(1)即可;
另一种是选择偶数个p,即
dp[p][j]+=powmod[cnt[p]1]×dp[p1][j]

然后这道题就做完啦!不过注意一个细节,这里不小心将0这种情况算了进去,所以还要-1。
另外开LL数组会爆空间。。

AC代码:(mmp调了半天发现质数表写错了。。)

#include <iostream>#include <cstdio>#include <cctype>#include <algorithm>using namespace std;typedef long long LL;inline LL read(LL &x) {    x = 0; char c = getchar();    while(!isdigit(c)) c = getchar();    while(isdigit(c)) x = x * 10 + c - '0', c = getchar();}const LL MOD = 1e9+7;int prime[19] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67};int s[73], cnt[73], powmod[100003], dp[73][(1 << 19)];inline void fenjie(LL x) {    int t = x;    //cout<<x<<" ";    for(int j = 0; j < 19; ++j) {        //cout<<x<<" "<<prime[i]<<endl;        while(t % prime[j] == 0)             s[x] ^= (1 << j), t /= prime[j];    }}int main() {    //freopen("pai.txt", "w", stdout);    LL n; read(n);    LL tmp;    for(int i = 1; i <= 70; ++i) fenjie(i);    //for(int i = 1; i <= 70; ++i) cout<<s[i]<<endl;    for(int i = 1; i <= n; ++i) read(tmp), cnt[tmp]++;    powmod[0] = 1;    for(int i = 1; i <= n; ++i) powmod[i] = ((1LL) * powmod[i - 1] * 2) % MOD;    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 ^ s[i]] = ((1LL)*dp[i][j ^ s[i]] + (1LL)*powmod[cnt[i] - 1] * dp[i - 1][j]) % MOD;                dp[i][j] = ((1LL)*dp[i][j] + (1LL)*powmod[cnt[i] - 1] * dp[i - 1][j]) % MOD;            }       }    //cout<<dp[1][0]<<endl;    cout<<(dp[70][0] - 1) % MOD<<endl;     return 0;}

参考资料:
https://www.cnblogs.com/widsom/p/7910385.html
https://www.cnblogs.com/chinacwj/p/7967796.html