正题
题目大意
给出n,m,kn,m,kn,m,k
v=∏i=1nai(ai∈N+,ai<=m)k(gcd(v,k)=1)v=\frac{\prod_{i=1}^na_i(a_i\in N_+,a_i<=m)}{k}(gcd(v,k)=1)v=k∏i=1nai(ai∈N+,ai<=m)(gcd(v,k)=1)
求aaa的方案个数mod10007mod\ 10007mod 10007的值
解题思路
fi,jf_{i,j}fi,j表示前i个数的乘积和k的最大公约数为k的第j个约数时的方案个数。
动态转移方程:
fi,j=∑k=1prcj%prck=0fi−1,k∗f1,prcnprcj/prckf_{i,j}=\sum_{k=1}^{prc_j\%prc_k=0}f_{i-1,k}*f_{1,prcn_{prc_j/prc_k}}fi,j=k=1∑prcj%prck=0fi−1,k∗f1,prcnprcj/prck
prciprc_iprci为k的第i个约数,prcniprcn_iprcni为i是k的第几个约数。
然后我们考虑如何求出1∼m1\sim m1∼m内有多少个数与k的最大公约数为k的第i个约数
也就是∑i=1m(gcd(i,k)==d)(d∣n)\sum_{i=1}^m (gcd(i,k)==d)(d|n)∑i=1m(gcd(i,k)==d)(d∣n)
首先
gcd(i,k)=d⇒gcd(i/d,k)=1gcd(i,k)=d\Rightarrow gcd(i/d,k)=1gcd(i,k)=d⇒gcd(i/d,k)=1(数论基础)
我们可以考虑用容斥,我们将重复偶数次的减去,重复奇数次的加上。
之后预处理一下prcimodprcj==0prc_i\ \ mod\ \ prc_j==0prci mod prcj==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]);
}