nssl1176-轨道【数论,Dp】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/82947042

正题


题目大意

给出 n , m , k n,m,k
v = i = 1 n a i ( a i N + , a i < = m ) k ( g c d ( v , k ) = 1 ) v=\frac{\prod_{i=1}^na_i(a_i\in N_+,a_i<=m)}{k}(gcd(v,k)=1)
a a 的方案个数 m o d   10007 mod\ 10007 的值


解题思路

f i , j f_{i,j} 表示前i个数的乘积和k的最大公约数为k的第j个约数时的方案个数。
动态转移方程:
f i , j = k = 1 p r c j % p r c k = 0 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_j\%prc_k=0}f_{i-1,k}*f_{1,prcn_{prc_j/prc_k}}
p r c i prc_i 为k的第i个约数, p r c n i prcn_i 为i是k的第几个约数。
然后我们考虑如何求出 1 m 1\sim m 内有多少个数与k的最大公约数为k的第i个约数
也就是 i = 1 m ( g c d ( i , k ) = = d ) ( d n ) \sum_{i=1}^m (gcd(i,k)==d)(d|n)
首先
g c d ( i , k ) = d g c d ( i / d , k ) = 1 gcd(i,k)=d\Rightarrow gcd(i/d,k)=1 (数论基础)
我们可以考虑用容斥,我们将重复偶数次的减去,重复奇数次的加上。
之后预处理一下 p r c i    m o d    p r c j = = 0 prc_i\ \ mod\ \ prc_j==0 的情况就好了。


code

#pragma GCC optimize(2)
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
%:pragma GCC optimize("-fgcse")
%:pragma GCC optimize("-fgcse-lm")
%:pragma GCC optimize("-fipa-sra")
%:pragma GCC optimize("-ftree-pre")
%:pragma GCC optimize("-ftree-vrp")
%:pragma GCC optimize("-fpeephole2")
%:pragma GCC optimize("-ffast-math")
%:pragma GCC optimize("-fsched-spec")
%:pragma GCC optimize("unroll-loops")
%:pragma GCC optimize("-falign-jumps")
%:pragma GCC optimize("-falign-loops")
%:pragma GCC optimize("-falign-labels")
%:pragma GCC optimize("-fdevirtualize")
%:pragma GCC optimize("-fcaller-saves")
%:pragma GCC optimize("-fcrossjumping")
%:pragma GCC optimize("-fthread-jumps")
%:pragma GCC optimize("-funroll-loops")
%:pragma GCC optimize("-fwhole-program")
%:pragma GCC optimize("-freorder-blocks")
%:pragma GCC optimize("-fschedule-insns")
%:pragma GCC optimize("inline-functions")
%:pragma GCC optimize("-ftree-tail-merge")
%:pragma GCC optimize("-fschedule-insns2")
%:pragma GCC optimize("-fstrict-aliasing")
%:pragma GCC optimize("-fstrict-overflow")
%:pragma GCC optimize("-falign-functions")
%:pragma GCC optimize("-fcse-skip-blocks")
%:pragma GCC optimize("-fcse-follow-jumps")
%:pragma GCC optimize("-fsched-interblock")
%:pragma GCC optimize("-fpartial-inlining")
%:pragma GCC optimize("no-stack-protector")
%:pragma GCC optimize("-freorder-functions")
%:pragma GCC optimize("-findirect-inlining")
%:pragma GCC optimize("-fhoist-adjacent-loads")
%:pragma GCC optimize("-frerun-cse-after-loop")
%:pragma GCC optimize("inline-small-functions")
%:pragma GCC optimize("-finline-small-functions")
%:pragma GCC optimize("-ftree-switch-conversion")
%:pragma GCC optimize("-foptimize-sibling-calls")
%:pragma GCC optimize("-fexpensive-optimizations")
%:pragma GCC optimize("-funsafe-loop-optimizations")
%:pragma GCC optimize("inline-functions-called-once")
%:pragma GCC optimize("-fdelete-null-pointer-checks")
#include<cstdio>
#include<cmath>
#include<algorithm>
#define BPM 10007
#define N 4010
using namespace std;
int n,m,k,frct,prit,fft[N],sum,m1;
int pri[N],frc[N],ff[N][N],ys[10000001],f[N][N];
void dfs(int x,int zf,int ans)//容斥
{
	if(x>prit) {
		sum+=m1/ans*zf;
		return;
	}
	dfs(x+1,zf,ans);
	dfs(x+1,-zf,ans*pri[x]);
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	int sk=sqrt(k);
	for(int i=1;i<=sk;i++)
	{
		if(k%i==0)
		{
		  frc[++frct]=i;
		  if(k/i>sk) frc[++frct]=k/i;
		}
	}//求约数
	sort(frc+1,frc+1+frct);
	int tmp=k;
	for(int i=2;i<=sk;i++)
	{
		if(tmp==1) break;
		if(tmp%i==0)
		{
		  pri[++prit]=i;
		  while(tmp%i==0) tmp/=i;
		}
	}//求质因子
	if(tmp!=1) pri[++prit]=tmp;
	sort(pri+1,pri+tmp+1);
	for(int i=1;i<=frct;i++)
	{
		ys[frc[i]]=i;
		sum=0;m1=m/frc[i];
		dfs(1,1,1);
		f[1][i]=sum%BPM;
	}//计算f[1]
	for(int i=1;i<=frct;i++)
	  for(int j=1;j<=i;j++)
	    if(frc[i]%frc[j]==0)
	      ff[i][++fft[i]]=j;//预处理关系
	for(int i=2;i<=n;i++)
	  for(int j=1;j<=frct;j++)
	  {
	  	  if(!fft[j]) continue;
	  	  for(int k=1;k<=fft[j];k++)
	  	    f[i][j]=(f[i][j]+f[i-1][ff[j][k]]*f[1][ys[frc[j]/frc[ff[j][k]]]])%BPM;
	  	    //动态转移
	  }
	printf("%d",f[n][frct]);
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/82947042