2018年10月5日提高组模拟赛 T3 轨道

版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82948857

大意

给定 n , k n,k ,求

v k = i = 1 n a i { g c d ( v , k ) = 1 } vk=\prod_{i=1}^{n}a_i\{gcd(v,k)=1\}

的方案数


思路

f i , j f_{i,j} 表示前 i i 个数的乘积与 k k 的 最大公约数为 k k 的第 j j 个约数 时的方案数
得到方程

f i , j = k = 1 p r c k p r c j f i 1 k × f 1 , p r c n p r c j / p r c k f_{i,j}=\sum_{k=1}^{prc_k|prc_j}f_{i-1}{k}\times f_{1,prcn_{prc_j/prc_k}}

p r c i prc_i 表示 k k 的第 i i 个约数, p r c n x prcn_x 表示 x x k k 的第几个约数

然后我们考虑如何求出 1 1 ~ m m 中有多少个数与 k k 的 最大公约数为 k k 的第 i i 个约数 即 f 1 , i f_{1,i}

然后莫比乌斯反演搞一搞

还是用容斥搞搞吧

最后处理 p r c j p r c i prc_j|prc_i 的情况即可


代码

#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]);//输出
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/82948857