1. 程式人生 > >[洛谷 1313]計算係數---二項式定理+快速冪+逆元(費馬小定理)

[洛谷 1313]計算係數---二項式定理+快速冪+逆元(費馬小定理)

題目描述

給定一個多項式(by+ax)^k,請求出多項式展開後x^n*y^m 項的係數。
輸入輸出格式
輸入格式:

輸入檔名為factor.in。

共一行,包含5 個整數,分別為 a ,b ,k ,n ,m,每兩個整數之間用一個空格隔開。

輸出格式:

輸出共1 行,包含一個整數,表示所求的係數,這個係數可能很大,輸出對10007 取模後的結果。

輸入輸出樣例
輸入樣例#1:

1 1 3 1 2

輸出樣例#1:

3

說明

【資料範圍】

對於30% 的資料,有 0 ≤k ≤10 ;

對於50% 的資料,有 a = 1,b = 1;

對於100%的資料,有 0 ≤k ≤1,000,0≤n, m ≤k ,且n + m = k ,0 ≤a ,b ≤1,000,000。

noip2011提高組day2第1題

分析

  神奇的數學題,直接提示了二項式定理( 其實就是說楊輝三角與二項式的係數相同,與C(n,m)相同 )
  由定理直接可以得出第x^n*y^m 項為:C(k,m)* (a*x)^n* (b*y)^m
  化簡可得其係數為: C(k,m) * a^n * b^m
  PS:由於二項式/楊輝三角性質可知 n+m=k 則 C(k,m)=C(k,k-n)=C(k,n)
  對於後二者,直接用快速冪(如果不知道,那麼88,走錯片場了)來求就行了.
  對於C(k,m),有兩種方法,其一是形如楊輝三角的遞推(C(k,m)=C(k-1,m-1)+C(k-1,m)).其二是考慮分解質因數+高精度

逆元(因為要取模) ( C(k,m)=k!/( (m-k)!*m! ) )
  對此僅給出逆元的解法(快速冪求逆元)
  

  1.逆元定義:對於正整數a和m,如果有 a*x≡1(mod m) { 即 a*x%m=1%m },那麼把這個同餘方程中的最小正整數解叫做a模m的逆元。
  2.費馬小定理:假如p是質數,且gcd(a,p)=1,那麼 a(p-1)≡1(mod p),即:假如a是整數,p是質數,且a,p互質(即兩者只有一個公約數1),那麼a的(p-1)次方除以p的餘數恆等於1
  3.對於除法取模來說 (a/b)%m=((a%mo)/(b%mo))%mo 是不存在的但對於+ - * ^ 來說成立,所以說可以考慮將 (a/b)%m轉化為 a*(b^-1)%mo . 當然,直接轉換是不存在的,於是藉助逆元進行轉化
  由於mo=10007為質數,與b=(m-k)!* m! 互質,所以,由費馬小定理可得: b^(mo-1)=1(mod mo) 又因為 b^-1*b=1 
  so b^(mo-1)=b^-1*b –> b^-1=b^(mo-2)
  於是 (令 b=(m-k)!* m!)
    C(k,m)%mo=( k!/b) %mo=( k! b^-1 )%mo=( k! *b^(mo-2))%mo=((k!%mo)

( (b%mo)^(mo-2)%mo))%mo
  其中b^(mo-2)可用快速冪解決

程式碼

數論程式碼一般都十分醜陋
貌似不用開long long的樣子

#include <cstdio>
#include <cstdlib>
#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define close fclose(stdin); fclose(stdout); 
using namespace std;

int mo=10007;
int jc[1005];

inline int read()
{
    int k=1;
    int sum=0;
    char c=getchar();
    for(;'0'>c || c>'9' ;c=getchar())
        if(c=='-') k=-1;
    for(;'0'<=c && c<='9';c=getchar())
        sum=sum*10+c-'0';
    return sum*k;
}

inline void write(int x)
{
    if(x<0) { putchar('-'); x*=-1; }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

inline int power(int x,int t)//快速冪  x^t
{
    if(t==1) return x;
    int s=power(x,t>>1);
    s=s*s%mo;
    return t&1?s*x%mo:s;
}

int main()
{
    open("1313");

    int a=read(),b=read(),k=read(),n=read(),m=read();
    jc[0]=1;
    for(int i=1;i<=k;++i) jc[i]=(jc[i-1]*i)%mo;//預處理階乘
    n%=mo; m%=mo;//由於n,m,a,b都可能比mo大,所以先取模
    a%=mo; b%=mo;

    int ans=1;
    //三部分
    ans=(jc[k]*power((jc[k-n]*jc[n])%mo,mo-2))%mo;
    ans=(ans*power(a,n))%mo;
    ans=(ans*power(b,m))%mo;

    write(ans);

    close;
    return 0;
}