CF895C Square Subsets(状压dp&组合数学)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF895C Square Subsets(状压dp&组合数学)相关的知识,希望对你有一定的参考价值。
CF895C Square Subsets(状压dp&组合数学)
平方数的一个性质是:所有质因子都出现偶数次。
且 a i ≤ 70 a_i \\le 70 ai≤70,小于 70 70 70的质因子只有 19 19 19个。
考虑把质因子当作状态,出现偶数次记为0,奇数次记为1。
因为权值很小,考虑以权值进行递推,令 d p [ i ] [ s ] dp[i][s] dp[i][s]。表示对于选择值域在 [ 1 , i ] [1,i] [1,i]状态为 s s s的方案数。
初始化 d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1。
预处理每个值出现的次数: c n t i cnt_i cnti。
对于当前第 i i i个值 。
若选择偶数个,对于质因子的奇偶性无影响。
可以选择 C c n t i 0 + C c n t i 2 + C c n t i 4 ⋯ = 2 c n t i − 1 C_{cnt_i}^0+C_{cnt_i}^2+C_{cnt_i}^4\\dots =2^{cnt_i-1} Ccnti0+Ccnti2+Ccnti4⋯=2cnti−1
d p [ i ] [ s ] + = d p [ i − 1 ] [ s ] × 2 c n t i − 1 dp[i][s]+=dp[i-1][s]\\times 2^{cnt_i-1} dp[i][s]+=dp[i−1][s]×2cnti−1
若选择奇数个,对于质因子有影响。
同理 C c n t i 1 + C c n t i 3 + C c n t i 5 ⋯ = 2 c n t i − 1 C_{cnt_i}^1+C_{cnt_i}^3+C_{cnt_i}^5\\dots =2^{cnt_i-1} Ccnti1+Ccnti3+Ccnti5⋯=2cnti−1
d p [ i ] [ s ⊕ k ] = d p [ i − 1 ] [ s ] × 2 c n t i − 1 dp[i][s\\oplus k]=dp[i-1][s]\\times 2^{cnt_i-1} dp[i][s⊕k]=dp[i−1][s]×2cnti−1
答案就是: d p [ m x ] [ 0 ] − 1 dp[mx][0]-1 dp[mx][0]−1
这个 − 1 -1 −1 是 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0] 空集。
因为每个状态由前一个状态转移 ,所以可以滚动优化空间。
然后可以预处理 2 x 2^{x} 2x 优化时间。
时间复杂度: O ( m x 2 19 ) O(mx 2^{19}) O(mx219)
#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define y0 pmt
#define y1 pmtpmt
#define x0 pmtQAQ
#define x1 pmtQwQ
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int > vi;
typedef pair<int ,int > pii;
typedef vector<pii> vii;
const int inf=0x3f3f3f3f, maxn=100007, mod=1e9+7;
const ll linf=0x3f3f3f3f3f3f3f3fLL;
const ll P=19260817;
const int p[]={2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67};//质数表
int n;
ll dp[2][1<<19];
ll cnt[75];
int I=0;
ll h[maxn];//2^k的表
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int rt;
scanf("%d",&rt);
cnt[rt]++;
}
h[0]=1;
dp[0][0]=1;
for(int i=1;i<=n;i++)h[i]=(h[i-1]<<1)%mod;
for(int i=1;i<=70;i++){
if(cnt[i]==0)continue;
I^=1;//滚动数组
memset(dp[I],0,sizeof(dp[I]));//注意初始化
for(int mask=0 ; mask< (1<<19) ; mask++){
int mask1=mask;
int x=i;
for(int j=0;j<19&&x>=p[j];j++){
while(x%p[j]==0)x/=p[j],mask1^=(1<<j);
}
(dp[I][mask1]+=1LL*dp[I^1][mask]*h[cnt[i]-1]%mod)%=mod;
(dp[I][mask]+=1LL*dp[I^1][mask]*h[cnt[i]-1]%mod)%=mod;
}
}
printf("%I64d\\n",(dp[I][0]-1+mod)%mod);
return 0;
}
另外此题,把每个数转换为他们的状态后,题意就相当于就选择集合使得异或和为0的子集个数。
这个可以用线性基做,因为线性基外的所有数都可以通过线性基得到。
所以他们异或和都是为0的,这与答案的关系是一对一映射的。
所以我们可以把这些状态插入线性基,最后答案就是线性基外所有的数组成的集合。
a n s = 2 n − c n t − 1 ans=2^{n-cnt}-1 ans=2n−cnt−1,减1是减去空集。
时间复杂度: O ( 20 n ) O(20n) O(20n)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e5;
const int P = 1e9 + 7;
using namespace std;
int n,a[N + 5],prime[N + 5],pcnt,vis[N + 5],val[N + 5],p[N + 5],cnt;
int mypow(int a,int x){int s = 1;for (;x;x & 1 ? s = 1ll * s * a % P : 0,a = 1ll * a * a % P,x >>= 1);return s;}
void prework()
{
for (int i = 2;i <= 70;i++)
{
if (!vis[i])
prime[++pcnt] = i;
for (int j = 1;prime[j] * i <= 70 && j <= pcnt;j++)
{
vis[i * prime[j]] = 1;
if (i % prime[j] == 0)
break;
}
}
}
void ins(int x)
{
for (int i = 22;i >= 0;i--)
if (x >> i & 1)
{
if (!p[i])
{
p[i] = x;
cnt++;
break;
}
x ^= p[i];
}
}
int main()
{
scanf("%d",&n);
for (int i = 1;i <= n;i++)
scanf("%d",&aCodeforces 895C - Square Subsets
Codeforces Round #448 (Div. 2)C. Square Subsets
cf660E Different Subsets For All Tuples
hdu 6125 Free from square (状压DP+分组背包)