有一天整数?!觉得自己太胖了,于是想把自己拆开来看看,结果一拆不得了,在拆的过程中他发现了一些人生的奥秘,并想让你也体验一下。
定义?(?, ?)表示将?拆成?个不同正整数的乘积的方案数,注意一种方案的排列仍然是同一种方案,也就是说2×3×5和5×2×3是同一种方案。
比如?(144,4) = 7,分别列出来就是:
144 = 1×2×4×18 = 1×2×8×9 = 1×2×3×24 = 1×2×6×12 = 1×3×4×12= 1×3×6×8 = 2×3×4×6
现在要你回答?(?!, ?) mod 10^9 + 7的值。
满足? ≤ 10000, ? ≤ 30。
由于要求互不相同,首先忽略大小限制,最后除掉 k ! k! k!即可。
把 k k k个数,看成是 k k k个点,两个数相同则连一条边。
我们要求没有连边的方案数。
发现所有方案连出来的图都是由若干完全图组成的。
考虑枚举连通块划分,强行令同一块内部相等,不同块之间没有限制。
对于一个连通块划分方案,将原来 k k k个点对应起来的方案数为 k ! ∏ i i ! c n t [ i ] c n t [ i ] ! \frac{k!}{\prod_{i}i!^{cnt[i]}cnt[i]!} ∏ii!cnt[i]cnt[i]!k!
计算方案数显然直接用背包就行了。
接下来考虑容斥。
考虑一个实际大小为 k k k的连通块。
在我们枚举一个大小为 t t t的连通块的时候会被算 S k , t S_{k,t} Sk,t次,其中 S k , t S_{k,t} Sk,t是第二类斯特林数。
我们需要每一个连通块大小都是 1 1 1的方案数。显然一个连通块的容斥系数就是斯特林反演的系数 ( − 1 ) i − 1 ( i − 1 ) ! (-1)^{i-1}(i-1)! (−1)i−1(i−1)!,其中 i i i是这个连通块的大小。一个图的容斥系数就是所有连通块系数的乘积。
暴力枚举 k k k的有序正整数拆分,然后算容斥系数,原图方案数,背包方案数,就没了。
代码:
#include
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
char c;
while(!isdigit(c=gc()));T num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int gi(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
cs int mod=1e9+7;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
return res;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
cs int N=1e4+7;
int p[N],pc,tim[N],ct[N],px;
bool mark[N];
inline void linear_sieves(int n){
for(int re i=2;i<=n;++i){
if(!mark[i])p[++pc]=i;
for(int re j=1;i*p[j]<=n;++j){
mark[i*p[j]]=true;
if(i%p[j]==0)break;
}
}
for(int re i=1;i<=pc;++i){
for(int re j=p[i];j<=n;j*=p[i])tim[i]+=n/j;
}
for(int re i=1;i<=pc;++i){
px=std::max(tim[i],px);
++ct[tim[i]];
}
}
int n,k,ans;
int g[N],fac[N],ifac[N],inv[N];
int cnt[N];
int f[10000];
inline int calc(int mx){
int res=1;
int up=px;
memset(f+1,0,sizeof(int)*up);
f[0]=1;
for(int re j=1;j<=mx;++j){
for(int re k=cnt[j];k;--k)
for(int re t=j;t<=up;++t)
Inc(f[t],f[t-j]);
}
for(int re i=px;i;--i)if(ct[i]){
Mul(res,power(f[i],ct[i]));
if(!res)return 0;
}
return res;
}
void dfs(int last,int rest,int coef){
if(rest==0){Inc(ans,mul(coef,calc(last)));return ;}
if(rest<last)return ;
for(int re i=last;i<=rest;++i){
++cnt[i];
int v=mul(coef,ifac[i]);
Mul(v,mul(inv[cnt[i]],g[i]));
dfs(i,rest-i,v);
--cnt[i];
}
}
signed main(){
#ifdef zxyoi
freopen("jdt.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
freopen("jdt.in","r",stdin);freopen("jdt.out","w",stdout);
#endif
#endif
scanf("%d%d",&n,&k);linear_sieves(n);
g[1]=1;for(int re i=2;i<=100;++i)g[i]=dec(0,mul(i-1,g[i-1]));
fac[0]=fac[1]=ifac[0]=1;for(int re i=2;i<=100;++i)fac[i]=mul(fac[i-1],i);
ifac[100]=power(fac[100],mod-2);for(int re i=99;i;--i)ifac[i]=mul(ifac[i+1],i+1);
inv[0]=inv[1]=1;for(int re i=2;i<=100;++i)inv[i]=mul(mod-mod/i,inv[mod%i]);
dfs(1,k,1);
cout<<ans<<"\n";
return 0;
}