1. 程式人生 > >數組(組合數學)

數組(組合數學)

span 100% 簡單 ios 格式 題意 ... clu using

數組(數學)

【問題描述】

fabdec 有?個長度為 n 的數組 \(a[]\)(下標 1-n), 初始時都是 0。 fabdec 隨機了?個 1 到 n 的隨機數 x,並且把 \(a[x]++\)
fabdec 重復了 m 次這樣的操作,然後數了?下數組??共有 k 個位置 為奇數。 fabdec 現在想問執? m 次操作,總共能?成多少種不同的數組使得恰 好有 k 個位置是奇數?
(兩個數組不同當且僅當兩個數組存在某個位置數組 的值不相同)
因為這個數字會很?,所以只需輸出這個答案除以 109 + 7 的余數。

【輸入格式】

??三個整數,n,m,k。

【輸出格式】

輸出包含?個整數,表?答案。

【樣例輸入】

2 3 1

【樣例輸出】

4

【數據規模及約定】

對於前 20% 的數據,1 ≤ n,m ≤ 4。
對於前 50% 的數據,1 ≤ n,m ≤ 2000。
對於前 100% 的數據,1 ≤ n,m ≤ 105, 0 ≤ k ≤ n。

看到這個題,我們化簡題意可得:
m分成n個數,要求這n個數中正好有k個奇數,問合法的排列有多少個。(也可以說成n個數的和為m)

我們考慮組合數的做法:
因為要求是k個奇數,所以我們要從n個數裏面選取k個數當作奇數,這一步是\(C_n^k\)的。
我們設這n個數分別為\(a_1,a_2,...,a_n\),那麽我們知道\[\sum_{i=1}^na_i=m\]
但是數字中有奇數有偶數不方便處理,所以我們考慮把所有數字都換成偶數,那麽在等式的左右兩邊同時減去k,我們就可以認為是\(m-k\)

分成了n個偶數。
又由於偶數除以2可能為奇數,可能為偶數,所以當我們在等式的左右兩邊同時除2的時候,該問題就轉化成了將\(\frac{m-k}{2}\)分成\(n\)份(其中可以每一份的個數可以為0),有多少種不同的分法(順序不同也視作不同)的問題了。運用到我們高中數學中的隔板法,我們可以知道這一步的答案是\(C_{\frac{m-k}{2}+n-1}^{n-1}\),所以運用乘法原理,我們可以知道答案是\[C_{n}^k\times C_{\frac{m-k}{2}+n-1}^{n-1}\]
之後就很簡單了,但是由於題目是在模意義下,所以要求逆元。逆元用費馬小定理+快速冪很容易就能求出來。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
// by zrt
using namespace std;

typedef long long LL;

LL n,m,k;
const LL mod=1e9+7;
LL pow(LL a,LL b,LL p){
    LL ret=1%p;
    while(b){
        if(b&1) ret=ret*a%p;
        b>>=1;
        a=a*a%p;
    }
    return ret;
}
LL fac(LL n){
    LL ret = 1%mod;
    for(int i=1;i<=n;i++) ret=ret*i%mod;
    return ret;
}
LL invfac(LL n){
    return pow(fac(n),mod-2,mod);
}
LL C(LL n,LL m){
    if(n<m)return 0;
    return fac(n)*invfac(m)%mod*invfac(n-m)%mod;
}
int main(){
    freopen("array.in","r",stdin);
    freopen("array.out","w",stdout);
    cin>>n>>m>>k;
    if(k>=m) cout<<0<<endl;
    else if((m-k)&1) cout<<0<<endl;
    else cout<< C(n,k)*C((m-k)/2+n-1,n-1)%mod<<endl;
    return 0;
}

數組(組合數學)