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,可以选任意两个 ,也可以三个都选( 不是平方数,但 是),实际上选两个 有 种方案,答案为 。
-
-1
小数据直接 暴力搜索即可。
考虑大数据,无法暴力枚举方案,显然是需要我们直接统计方案数的,观察到 。
很明显是希望我们从因子的角度去考虑,对于一个完全平方数而言,质因数分解后的所有质因子个数必然是偶数
那么对于两个数字相乘,实际上就是将所有质因子的个数相加。
那么如果我们可以将所有数字进行质因数分解,就可以发现可行方案就是选几个数的质因子数量依次相加,如果所有质因子的个数均为偶数,则就是一种方案。
因为我们只关心质因子的个数是奇数还是偶数,并且 最大只有 ,而 以内的质因子个数只有 个。
我们完全可以直接将质因子进行状态压缩,只保留奇偶性。
那么对于一个数字 状压后得到质因子奇偶性的状态 ,那我们需要找到的就是 在不进位的情况下结果为 的方案数。
不进位加法那就可以直接异或,也就是我们只需要找到所有 的方案数。
显然可以直接进行 。
设 表示前 个数字得到质因子状态 的方案数。
那么转移即为 ,直接求和即可。
这里建议使用 “到哪去” 的方向去求解,这样可以减少计算的次数,且保证正确性。
那么可以发现总体复杂度就是 ,结合数据范围中当 时 可以反向验证此做法就是出题者希望的解法。
在考虑到复杂度以后要注意,前面的特殊情况(测试点 )无法使用此做法通过,需要进行特判。
这部分实际上就是每个质数可以选偶数个(包含 ) ,在 个数字里选偶数个数字的方案数就是 ,总方案数的一半。
那么把所有质因子个数的 做乘积,最后 即可(去掉全不选的情况)。
- 1
信息
- ID
- 2746
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- (无)
- 递交数
- 47
- 已通过
- 14
- 上传者