1. 程式人生 > >UPC-6016 微信群(O(n)組合數模板 線性求組合數)

UPC-6016 微信群(O(n)組合數模板 線性求組合數)

題目描述
眾所周知,一個有著6個人的宿舍可以有7個微信群(^_^,別問我我也不知道為什麼),然而事實上這個數字可以更大,因為每3個或者是更多的人都可以組建一個群,所以6個人最多可以組建42個不同的群。
現在,已知一間宿舍有N個人,並且每至少K個人都可以組建一個微信群,那麼他們最多可以組建多少個不同的微信群?
輸入
一行兩個整數N和K,表示宿舍中的人數和最少能夠組建微信群的人數
輸出
一行一個整數,即最多能組建多少個不同的微信群,由於這個數字很大,請輸出對10^9+7求餘後的結果
樣例輸入
6 3
樣例輸出
42
提示
對於30%的資料,3<=N<=10^3
對於60%的資料,3<=N<=10^6
對於100%的資料,3<=N<=10^9,3<=K<=10^5

此處記住一個定理 組合數 C(n,0)+C(n,1)+……..+C(n,n) =2^n 以此來計算第N個組合數所有可能之和

組合數模板:
C(n,m)= n! / m!*(n-m)!
預處理n(1e5)的階乘
預處理m(1e5)階乘的逆元

然後對所有組合數C(n,m) 直接計算分母(n!) × 逆元m!× 逆元(n-m)!

對於此題中,要求從C(n,k)的組合數求和到C(n,n),直接用組合數模板然後for迴圈求和會超時,因為會有可能從3遍歷到1e9
因為k只到1e5,因此可以利用組合數 總和 2^n 減去前半段0 ~ k-1較短的遍歷求出總和,得到ans,後半段的組合數總和

根據組合數n的規律,對於C(n,i) i=0~k-1 可以根據當前值遞推出i+1的組合數,將當前值 乘 (n-i+1) 也就是當前分母-1,分子 乘 i 即可得到下一個組合數的值。
這樣可以以線性複雜度得到一個組合數N的所有組合值。

#include<stdio.h>
#include<algorithm>
#define LL long long
using namespace std;
const LL MOD=1e9+7;
LL poww(LL x, LL n)///快速冪
{
    LL res = 1;
    while(n)
    {
        if
(n & 1) res =res*x%MOD; x = x * x % MOD; n >>= 1; } return res; } ///組合數模板 LL p[100005],f[100005];///p為階乘陣列,f為階乘的逆元 void init()///預處理 { p[0]=1; for (int i=1;i<=100000;++i)///計算階乘 p[i]=p[i-1]*i%MOD; f[0]=1; for (int i=1;i<=100000;++i)///計算逆元 f[i]=poww(p[i],MOD-2); return ; } LL comb(int n,int m)///計算組合數 { return (f[m]*f[n-m])%MOD*p[n]%MOD; }///分子----> n! ///分母----> m!*(n-m)! 計算逆元后相乘得到C(n,m)的組合數 int main() { LL k,n; while(scanf("%lld%lld",&n,&k)!=EOF) { LL ans=1; LL sum=poww(2,n);/// 組合數總和!!!C(n,0)+C(n,1)+......+C(n,n)=2^n LL tmp=n; for(int i=2;i<=k;i++) { ans=(ans+tmp)%MOD; // printf("===%lld=====%lld\n",tmp,ans); tmp=((tmp*(n-i+1))%MOD*poww(i,MOD-2))%MOD;///線性計算組合數,分子*n-i+1,分母*i } ans=(sum-ans+MOD)%MOD;///減法運算取模,加上MOD再對MOD取模 printf("%lld\n",ans); }///因為題目中計算K到N(1e9) 的組合數計算會超時,因此利用n的組合數總和2^n減去從0到k-1的組合數之和(較短段),得到後半段k到n組合數之和(較長段) } //LL comb(LL n, LL m)///另一種組合數求法,在數塔中斜向求組合數 //{ // if(m > n) return 0; // LL ret = 1; // m = min(n - m, m); // for(int i = 1; i <= m; i ++) // { // LL a = (n + i - m) % MOD; // LL b = i % MOD; // ret = ret * (a * mod_pow(b, MOD - 2) % MOD) % MOD; // } // sum=(sum%MOD+ret%MOD)%MOD; //} // //LL Lucas(LL n, LL m) //{ // if(m == 0) return 1; // return comb(n % MOD, m % MOD) * Lucas(n / MOD, m / MOD) % MOD; //}