4 条题解
-
2
Sam的完全平方数 题解
核心思路
注意到,一个完全平方数的所有质因子的次数都是偶数。因此,我们只需要关心每个质因子次数的奇偶性,而不需要关心具体次数。
由于 ,而 以内的质数只有 个:$2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67$。所以我们可以用一个长度为 的 串(状态压缩)来表示一个数所有质因子次数的奇偶性。
状态设计
设 表示选出若干个数,使得这些数的质因子奇偶性状态异或起来等于 的方案数。
- 初始状态:,表示什么都不选,状态为 。
- 对于每个数 ,计算它的状态 (每个质因子次数模 的结果组成的二进制数)。
- 转移:$dp_{new}[mask] = dp_{old}[mask] + dp_{old}[mask \oplus mk]$。即要么不选 ,要么选 并将状态异或上 。
为什么这样是对的?
因为异或运算满足交换律和结合律,且两个数相乘时,对应质因子的次数是相加的。而我们只关心奇偶性,所以加法在模 意义下就是异或。
同时,一个若最终凑出的01串为 ,不论是否是通过加入 而来的,都满足是合法的方案,对后续答案没有影响,也就是无后效性,可以使用滚动数组优化。
所以最终答案为 (减去什么都不选的那一种方案)。
关键注意
若当前出现的所有数的质因子中都没有某一个质因子,那么这个质因子在转移时可以不跑,见代码里的 变量。
在此基础上,将 从小到大排序,可以在 均为质因子时,跑的飞快。
代码实现
#include<bits/stdc++.h> using namespace std; const int N=350,MOD=1e9+7; int n,a[N],dp[1<<19],ndp[1<<19],mxp=0,pri[19]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67}; int main(){ freopen("square.in","r",stdin); freopen("square.out","w",stdout); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int T; cin>>T; while(T--){ for(int i=0;i<(1<<19);i++){ ndp[i]=dp[i]=0; } cin>>n; dp[0]=ndp[0]=1; mxp=0; for(int x=1;x<=n;x++){ cin>>a[x]; } sort(a+1,a+n+1); for(int x=1;x<=n;x++){ int mk=0; for(int i=0;i<19 && pri[i]<=a[x];i++){ if(a[x]%pri[i]!=0){ continue; } mxp=max(mxp,i+1); int cnt=0,p=pri[i]; while(a[x]%p==0){ a[x]/=p; cnt++; } if(cnt%2==1){ mk|=(1<<i); } } for(int mask=0;mask<(1<<mxp);mask++){ ndp[mask]=(ndp[mask]+dp[mask^mk])%MOD; } for(int mask=0;mask<(1<<mxp);mask++){ dp[mask]=ndp[mask]; } } cout<<(dp[0]+MOD-1)%MOD<<endl; } return 0; }复杂度分析
- 时间复杂度:,其中 是 以内的质数个数。
- 空间复杂度:。
样例验证
- 第一组:
3 3 8,只有选3,3得到 ,答案为 。 - 第二组:
2 4 8 16,有 种方案,答案为 。 - 第三组:
3 3 3,可以选任意两个 ,也可以三个都选( 不是平方数,但 是),实际上选两个 有 种方案,答案为 。
信息
- ID
- 2746
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- (无)
- 递交数
- 47
- 已通过
- 14
- 上传者