有一天整數𝑛!覺得自己太胖了,於是想把自己拆開來看看,結果一拆不得了,在拆的過程中他發現了一些人生的奧祕,並想讓你也體驗一下。
定義𝐹(𝑛, 𝑘)表示將𝑛拆成𝑘個不同正整數的乘積的方案數,注意一種方案的排列仍然是同一種方案,也就是說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。
題解:
由於要求互不相同,首先忽略大小限制,最後除掉即可。
把個數,看成是個點,兩個數相同則連一條邊。
我們要求沒有連邊的方案數。
發現所有方案連出來的圖都是由若干完全圖組成的。
考慮枚舉連通塊劃分,強行令同一塊內部相等,不同塊之間沒有限制。
對於一個連通塊劃分方案,將原來個點對應起來的方案數爲
計算方案數顯然直接用揹包就行了。
接下來考慮容斥。
考慮一個實際大小爲的連通塊。
在我們枚舉一個大小爲的連通塊的時候會被算次,其中是第二類斯特林數。
我們需要每一個連通塊大小都是的方案數。顯然一個連通塊的容斥係數就是斯特林反演的係數,其中是這個連通塊的大小。一個圖的容斥係數就是所有連通塊係數的乘積。
暴力枚舉的有序正整數拆分,然後算容斥係數,原圖方案數,揹包方案數,就沒了。
代碼:
#include<bits/stdc++.h>
#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;
}