1 条题解
-
0
自动搬运
来自洛谷,原作者为

Zesty_Fox
Life, the Universe, and Everything.搬运于
2025-08-24 22:19:12,当前版本为作者最后更新于2020-06-16 16:57:14,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
安利一波博客:点这里
首先,题目说了最多个质因数。
如此小的数据范围,不是状压还是啥?
然后,我们可以发现一个性质:只要两个因数有相同的质因数(不管次数是多少),两者就不互质。
这启示我们用一个二进制数来表示一类的因数,每个二进制位表示对应质因数的状态,表示有,表示没有。
举个例子:$N=12156144=2^4 \times 3 \times 7 \times 11^2 \times 13 \times 23$
那么,每个二进制数中,第位表示是否有质因数,第位表示是否有质因数,第位表示是否有质因数......以此类推。
比如说,的因数,应该对应的是,即它属于第类数。
状压以后,不难用乘法原理求出每类数的个数。接着,我们考虑如何求出答案。
我们用一个三进制数来表示当前状态。第位表示当前第类数的数量+与第类不互质的数的个数。状态转移就很好写了。枚举当前增加哪一类数,直接转移即可。
最后有个要点:可以改成四进制数来写,并用一个来存,简化代码。
Code:
#include <iostream> #include <cstdio> #include <map> #define int long long using namespace std; typedef __uint128_t int128; const int MOD=1e9+7; int n,zys[105],cs[105],cnt,sum[1<<8],ans; map<int128,int> m; //存状态 int dfs(int128 stat){ if(m.count(stat)) return m[stat];//记忆化 int res=1; for(int i=1;i<(1<<cnt);i++){ //枚举增加的数是哪一类 int cur=(stat>>(i<<1))&3; //获取当前这一类数的情况 int128 tmp=stat; if(cur>1) continue; //如果已经有数与它不互质了,显然不能再增加此类数 for(int j=0;j<(1<<cnt);j++){ if(!(i&j)) continue; //验证每一类数是否与所选的数互质 //如果这两类数有相同的质因子,则按位与肯定不为0 if((stat>>(j<<1)&3)<2) stat+=((int128)1<<(j<<1)); } res=(res+dfs(stat)*sum[i])%MOD; stat=tmp; } return m[stat]=res; } signed main(){ scanf("%lld",&n); int tmp=n; for(int i=2;i*i<=tmp;i++){ if(tmp%i==0){ zys[++cnt]=i; while(tmp%i==0) tmp/=i,cs[cnt]++; //分解质因数 //zys:存储从小到大不同的质因子 //cs:每一个质因子的次数 } } if(tmp>1) zys[++cnt]=tmp,cs[cnt]++; for(int i=0;i<(1<<cnt);i++){ sum[i]=1; for(int j=1;j<=cnt;j++){ if(i&(1<<j-1)) sum[i]=(sum[i]*cs[j])%MOD; //乘法原理,计算每一类数的个数 } } ans=dfs((int128)0); cout<<(ans-1+MOD)%MOD<<endl;//空集不能算,所以答案-1 return 0; }
- 1
信息
- ID
- 5302
- 时间
- 650ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者