版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82948857
大意
给定 ,求
的方案数
思路
设
表示前
个数的乘积与
的 最大公约数为
的第
个约数 时的方案数
得到方程
表示 的第 个约数, 表示 是 的第几个约数
然后我们考虑如何求出 ~ 中有多少个数与 的 最大公约数为 的第 个约数 即
然后莫比乌斯反演搞一搞
还是用容斥搞搞吧
最后处理 的情况即可
代码
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cmath>
#define ymw 10007
using namespace std;int m1,sum,f[3001][4001],mp[10000001],n,m,k;
vector<int>fac,kpri,fsf[4001];
inline void dfs(register int dep,register int p_m,register int ble)//荣斥
{
if(dep>kpri.size()) {sum+=m1/ble*p_m;return;}
dfs(dep+1,p_m,ble);dfs(dep+1,-p_m,ble*kpri[dep-1]);return;
}
signed main()
{
scanf("%d%d%d",&n,&m,&k);
int sqrk=sqrt(k);
for(register int i=1;i<=sqrk;i++)//fac取约数
if(!(k%i))
{
fac.push_back(i);
if(k/i>sqrk) fac.push_back(k/i);
}
sort(fac.begin(),fac.end());
int x=k;
for(register int i=2;i<=sqrk;i++)//kpri取质因子
{
if(x==1) break;
if(!(x%i))
{
kpri.push_back(i);
while(!(x%i)) x/=i;
}
}
if(x!=1) kpri.push_back(x);
sort(kpri.begin(),kpri.end());
for(register int i=0;i<fac.size();i++)//荣斥预处理f[1][i]
{
mp[fac[i]]=i;//映射
sum=0;m1=m/fac[i];
dfs(1,1,1);
f[1][i]=sum%ymw;
}
for(register int i=0;i<fac.size();i++)
for(register int j=0;j<=i;j++)
if(!(fac[i]%fac[j])) fsf[i].push_back(j);//预处理k的约数中最大公约数为k的约数的组合
for(register int i=2;i<=n;i++)
for(register int j=0;j<fac.size();j++)
{
if(fsf[j].empty()) continue;
for(register int k=0;k<fsf[j].size();k++)
(f[i][j]+=f[i-1][fsf[j][k]]*f[1][mp[fac[j]/fac[fsf[j][k]]]])%=ymw;//动态转移
}
printf("%d\n",f[n][fac.size()-1]);//输出
}