【校內模擬】簡單題(容斥)(斯特林反演)(揹包)

有一天整數𝑛!覺得自己太胖了,於是想把自己拆開來看看,結果一拆不得了,在拆的過程中他發現了一些人生的奧祕,並想讓你也體驗一下。

定義𝐹(𝑛, 𝑘)表示將𝑛拆成𝑘個不同正整數的乘積的方案數,注意一種方案的排列仍然是同一種方案,也就是說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!即可。

kk個數,看成是kk個點,兩個數相同則連一條邊。

我們要求沒有連邊的方案數。

發現所有方案連出來的圖都是由若干完全圖組成的。

考慮枚舉連通塊劃分,強行令同一塊內部相等,不同塊之間沒有限制。

對於一個連通塊劃分方案,將原來kk個點對應起來的方案數爲k!ii!cnt[i]cnt[i]!\frac{k!}{\prod_{i}i!^{cnt[i]}cnt[i]!}

計算方案數顯然直接用揹包就行了。

接下來考慮容斥。

考慮一個實際大小爲kk的連通塊。

在我們枚舉一個大小爲tt的連通塊的時候會被算Sk,tS_{k,t}次,其中Sk,tS_{k,t}是第二類斯特林數。

我們需要每一個連通塊大小都是11的方案數。顯然一個連通塊的容斥係數就是斯特林反演的係數(1)i1(i1)!(-1)^{i-1}(i-1)!,其中ii是這個連通塊的大小。一個圖的容斥係數就是所有連通塊係數的乘積。

暴力枚舉kk的有序正整數拆分,然後算容斥係數,原圖方案數,揹包方案數,就沒了。


代碼:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章