題目
簡化題意:1~n個不同盒子,放入m個相同小球,盒子可空,但是1號盒子的球數必須嚴格最多,問合法方案數。
分析
簡化問題
如果沒有1號盒子嚴格最多的限制,就變成了一個插板的問題。
也就是這類不定方程求非負整數解的問題,插板法解決。
容斥
如果枚舉猴王(1號盒子)的球數爲i
那麼問題就變成不定方程並且,加了一個上限。
上限通常會用容斥轉化成下限,下限繼續插板解決。
枚舉至少k個盒子的球數,然後容斥解決問題。
假設現在是要求至少k個盒子的球數不少於1號盒子,那麼就先選出k個盒子,也就是
然後向每個選出來的盒子放i個球,剩下的個球隨便放入個盒子裏面
然後運用插板法,種方法。
子問題得證
現在我們解決了已知1號盒子球數i、至少k個盒子的球數不小於i的方案個數。
然後就用容斥解決。
容斥其實和二項式反演有關,有的時候容斥不太好理解或者好想到,就先想到二項式反演再得到容斥的式子。
這裏就先給出容斥的形式,二項式反演後文會講解。
確定i的時候,令表示至少k個盒子球數不少於i的方案數
那麼對於i,答案爲:
相關代碼
for(int i=1;i<=n;i++){//枚舉猴王得到的桃子個數
long long id=1;
for(int k=0;n-(k+1)*i>=0;k++){//枚舉至少有k只小猴子得到的桃子個數>猴王
ans+=id*C(m-1,k)%mod*C(n-(k+1)*i+m-2,m-2)%mod;
ans%=mod;
id=-id;
}
}
代碼
下附AC代碼:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int read(){
char s;
int x=0,f=1;
s=getchar();
while(s<'0'||s>'9'){
if(s=='-')f=-1;
s=getchar();
}
while(s>='0'&&s<='9'){
x*=10;
x+=s-'0';
s=getchar();
}
return x*f;
}
const long long mod=1000000007;
const int N=300000;
long long qpow(long long a,long long b){
if(b==0)return 1;
long long rec=qpow(a,b/2)%mod;
if(b&1)return rec*rec%mod*a%mod;
return rec*rec%mod;
}
long long calc[N],inv[N];//階乘 階乘逆元
void init(int n){
calc[0]=1;
for(long long i=1;i<=n;i++){
calc[i]=calc[i-1]*i%mod;
}
inv[n]=qpow(calc[n],mod-2);
for(long long i=n-1;i>=0;i--){
inv[i]=inv[i+1]*(i+1)%mod;
}
return;
}
long long C(int a,int b){//組合數
if(b>a)return 0;
return calc[a]*inv[b]%mod*inv[a-b]%mod;
}
int main(){
int T=read();
init(N-5);
while(T--){
int n,m;
n=read(),m=read();//n個桃子,m個猴子
if(m==1||n==1){
puts("1");
continue;
}
long long ans=0;
for(int i=1;i<=n;i++){//枚舉猴王得到的桃子個數
long long id=1;
for(int k=0;n-(k+1)*i>=0;k++){//枚舉至少有k只小猴子得到的桃子個數>猴王
ans+=id*C(m-1,k)%mod*C(n-(k+1)*i+m-2,m-2)%mod;
ans%=mod;
id=-id;
}
}
ans=(ans%mod+mod)%mod;
printf("%lld\n",ans);
}
}
相關知識
這道題挺綜合的,考了很多組合基本知識
插板法介紹
插板法是解決這麼一類問題:不定方程的非負整數解的問題
其中mn爲正整數
我們把題目轉化成求正整數解,也就是每個數先+1:
令
這樣取值範圍也向上加一
那我們把它看成,m+n個相同小球,分成非空的n堆,這樣從左往右n堆分別代表n個x,這就把解轉化爲劃分方式(注意,與第二類斯特林數不同,這個盒子是相同的,沒學過的直接忽略這句話)
那麼由於現在是非空的,就有了個空隙,插入個板子,就是組合數了
這就是插板法解決不定方程問題。
二項式反演介紹
具體內容見:二項式反演
那麼這道題怎麼用二項式反演呢?
其實前面的從個盒子裏面選個盒子,就蘊含了二項式反演。
當ik確定的時候(一號盒子有i個球,後m-1個盒子裏有k個盒子球數不少於i)
我們令表示恰好k個盒子的球數不少於i
表示至少k個盒子球數不少於i
那麼這裏的g函數就和容斥的f函數不一樣了。
我就需要欽點k個盒子而不是自由排列組合選出k個盒子。
我欽點了前k個也就是這幾個盒子每個先放進去i個球,然後剩下的插板解決。
這樣才能保證g函數表示至少k個盒子而不會算重。
爲什麼要乘組合數呢?
我們來看:
畫個圖輔助理解:我們看藍色點,表示恰好3個。那麼綠、黃、紅都代表至少兩個。
那麼在f函數裏面,恰好3個的一種方案:藍色點,在至少兩個裏面出現了三次。
因爲我們回到“至少”函數的推導:我們欽點一部分作爲一開始各分發i個小球的,剩下球隨便給,黃綠紅分別代表欽點的方案,剩下的一個點代表隨便給球出現的,那麼一個藍色就對應了三種“至少”方案。
因此,可以得到上面的表達式。
那麼上面那個表達式代入二項式反演得到容斥的那個式子:
小總結
其實發現二項式反演和容斥的最終結果完全一樣。
容斥選擇至少k個不小於i的盒子直接用組合選。
但是二項式反演要欽點前k個,然後在“至少”和“恰好”之間轉化的時候加上第一個組合數。
看似多此一舉,實則在以後的難題裏運用更廣泛。
總結
這道題不失爲一道組合好題
它綜合運用多種知識,並不難。
這道題用二項式反演其實大材小用了,網上做法很多都是直接容斥。
但是以後遇到更難的題目的時候,想想二項式反演來輔助得到容斥結果也未嘗不可。
推薦題目:已經沒有什麼好害怕的了
附帶此題題解:題解
謝謝觀看,敬請指正。