ACM-組合數學完全總結(知識點+模板)


目錄

  1. 排列
    1.1不可重排列
    1.2可重排列
    1.3圓排列
    1.4不盡相異元素全排列
    1.5多重集的排列

  2. 組合
    2.1不可重組合數
    2.2可重組合
    2.3不相鄰組合
    2.4多重集的組合
    2.5常用組合數公式
    2.6組合數取模(模板)

  3. 常用公式及定理
    3.1二項式定理
    3.2鴿巢原理
    3.3常見恆等式
    3.4帕斯卡恆等式
    3.5盧卡斯定理推論
    3.6容斥原理
    3.7錯排問題

  4. 常見數列及其性質
    4.1斐波那契數列
    4.2卡特蘭數列

  5. 遞推方程
    5.1線性遞推方程
    5.2非線性遞推方程
    5.3求解遞推方程(模板)

  6. 母函數
    6.1普通母函數
    6.2指數型母函數
    6.3整數拆分

  7. Polya計數

  8. 快速傅里葉(FFT)



一.排列

1.不可重排列數:

這里寫圖片描述

若n和r都是整數,且0<=r<=n,有 這里寫圖片描述

2.可重排列數:

從n個物品中可重復的取k個的排列數為:n^k

3.圓排列:

n個不同物品中取m個的圓排列 這里寫圖片描述

4.不盡相異元素全排列:

這里寫圖片描述

5.多重集的排列

(1)設元素a1,a2,…,an互不相同,從無限多重集{∞a1,∞a2,…,∞*an}中取r個元素的排列數為n^r
(2)設元素a1,a2,…,an互不相同,從有限多重集{k1 * a1,k2 * a2,…,kn * an}中取r個元素的排列數為n^r,各k均大於等於r
(3)設元素a1,a2,…,an互不相同,從有限多重集{k1 * a1,k2 * a2,…,kn * an}中元素的全排列為[(k1+k2+…+kn)! / k1! * k2! * … * kn!]
(4)設元素a1,a2,…,an互不相同,從有限多重集{k1 * a1,k2 * a2,…,kn * an}中取r個元素,至少存在一個ki< r時,排列數為 [r * (r!/k1! * k2! * … * kn!)]
即指數型母函數G(x)=1+x/1!+x^2 /2!+…+x^ki/ki! 中 x^r的系數


二.組合

1.不可重組合數:

這里寫圖片描述

2.可重組合數:

這里寫圖片描述

3.不相鄰組合

4.多重集的組合

(1)設元素a1,a2,…,an互不相同,從無限多重集{∞* a1,∞* a2,…,∞* an}中取r個元素的組合數為C(n+r-1,r)
(2)設元素a1,a2,…,an互不相同,從有限多重集{k1* a1,k2* a2,…,kn* an},各k均大於等於r
從中取r個元素的組合數為C(n+r-1,r)
(3)設元素a1,a2,…,an互不相同,從有限多重集{k1* a1,k2* a2,…,kn* an}中取r個元素,至少存在一個ki < r時
令母函數G(x)=1+x+x^2 +…+x^ki ,i=1,2…n,G(x)中x^r的系數即為所求

5.常用組合數公式

(1)C(n,m)=C(n-1,m)+C(n-1,m-1)
(2)C(n,m)=C(n,n-m)
(3)C(n,m+1)=(n-m)/(m+1)*C(n,m)

6.組合數取模
typedef long long LL;
const LL maxn(1000005), mod(1e9 + 7);
LL Jc[maxn];
void calJc()    //求maxn以內的數的階乘
{
    Jc[0] = Jc[1] = 1;
    for(LL i = 2; i < maxn; i++)
        Jc[i] = Jc[i - 1] * i % mod;
}
/* //拓展歐幾里得算法求逆元 void exgcd(LL a, LL b, LL &x, LL &y) //拓展歐幾里得算法 { if(!b) x = 1, y = 0; else { exgcd(b, a % b, y, x); y -= x * (a / b); } } LL niYuan(LL a, LL b) //求a對b取模的逆元 { LL x, y; exgcd(a, b, x, y); return (x + b) % b; } */
//費馬小定理求逆元
LL pow(LL a, LL n, LL p)    //快速冪 a^n % p
{
    LL ans = 1;
    while(n)
    {
        if(n & 1) ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}
LL niYuan(LL a, LL b)   //費馬小定理求逆元
{
    return pow(a, b - 2, b);
}
LL C(LL a, LL b)    //計算C(a, b)
{
    return Jc[a] * niYuan(Jc[b], mod) % mod
        * niYuan(Jc[a - b], mod) % mod;
}

三.常用公式及定理

1.二項式定理:

這里寫圖片描述

2.鴿巢原理:將n+1個物品放到n個抽屜中,有一個抽屜至少有兩個物品

3.常見恆等式:
這里寫圖片描述

4.帕斯卡恆等式:

這里寫圖片描述

5.Lucas定理推論:C(n,m)為奇數 <=> n&m=m
6.容斥原理:

這里寫圖片描述
這里寫圖片描述

7.錯排問題:

考慮一個有n個元素的排列,若一個排列中所有的元素都不在自己原來的位置上,那么這樣的排列就稱為原排列的一個錯排。 n個元素的錯排數記為D(n)
這里寫圖片描述

簡化公式:

這里寫圖片描述
遞推公式:
Dn=(n-1)(Dn-1+Dn-2) n>3, D1 = 0 , D2 = 1


四.常見數列及其性質

1.斐波那契數列:

1.1定義:

Fi=Fi-1+Fi-1,F1=F2=1的數列{Fn}稱為斐波那契數列,Fn為斐波那契數

1.2通項公式:

這里寫圖片描述

1.3特殊形式公式:

Fn ≡ 276601605(691504013^n -308495997^n)(mod (1e9+9)) 由二次剩余推導

1.4性質:

1.平方與前后項:從第二項開始,每個奇數項的平方都比前后兩項之積少1,每個偶數項的平方都比前后兩項之積多1
2.與集合子集:Fn+2同時也代表了集合{1,2,…,n}中所有不包含相鄰正整數的子集個數
3.奇數項求和:F1+F3+F5+…+F(2n-1) = F(2n)-F2+F1
4.偶數項求和:F2+F4+F6+…+F(2n) = F(2n+1)-F1
5.平方求和:F1^2 +F2^2 +…+Fn^2 = Fn*F(n+1)
6.兩倍項關系:F(2n)/Fn = F(n-1)+F(n+1)
7.F(n-1)*F(n+1)-Fn^2 = (-1)^n
8.質數數量:
每3個連續的數中有且只有一個被2整除
每4個連續的數中有且只有一個被3整除,
每5個連續的數中有且只有一個被5整除,
每6個連續的數中有且只有一個被8整除,
每7個連續的數中有且只有一個被13整除,
每8個連續的數中有且只有一個被21整除,
每9個連續的數中有且只有一個被34整除
9.尾數循環:
個位數每60步一循環,后兩位數每300步一循環,后三位數,每1500步一循環,后4位數每15000步一循環,后5位數每150000步一循環

2.卡特蘭數列:

2.1計算公式:

h(n)=C(2n,n)/(n+1)
h(n)=C(2n,n)-C(2n,n-1)

2.2遞推公式:

h(n) = h(0)*h(n-1) + h(1)h(n-2) + … + h(n-1)h(0) (n>=2)
h(n)=h(n-1)
((4
n-2)/(n+1))
這里寫圖片描述
這里寫圖片描述

2.3常見用法:

卡特蘭數的應用都可以歸結到一種情況:有兩種操作,分別為操作一和操作二,它們的操作次數相同,都為 N,且在進行第 K 次操作二前必須先進行至少 K 次操作一,問有多少中情況?結果就Catalan(N)。

1.給定n個數,有多少種出棧序列
2.n個結點的二叉樹有多少種構型
3.有n+1個葉子的滿二叉樹的個數
4.在nn的格子中,只在下三角行走,每次橫或豎走一格,有多少種走法
5.將一個凸n+2邊型剖分成三角形的方法數
6.在圓上選擇2n個點,將這些點成對相連使得到的n條線段不相交的方法數
7.n個長方形填充一個高度為n的階梯狀圖形的方法數
8.由n個+1和n個-1組成的排列中,滿足前綴和>=0的排列數
9.括號化問題,左括號和右括號各有n個時,合法的括號表達式的種類
10.有n+1個數連乘,乘法順序的種類
11.n位二進制中,有m個0,其余為1,有h[C(n,m)-C(n,m-1)]種
12.將有2n個元素的集合中的元素兩兩分為n個子集,若任意兩個子集都不交叉,那么我們稱此划分為一個不交叉划分。此時不交叉的划分數是Catalan(N)
13.n層的階梯切割為n個矩形的切法數也是Catalan(N)。
14.在一個2
n的格子中填入1到2n這些數值使得每個格子內的數值都比其右邊和上邊的所有數值都小的情況數也是Catalan(N)。

求n<=35的卡特蘭數
void init()
{
    int i,j;
    LL h[36];
    h[0]=h[1]=1;
    for(i=2;i<36;i++)
    {
      h[i]=0;
    for(j=0;j<i;j++)
            h[i]=h[i]+h[j]*h[i-j-1];
    }
}
求n>35卡特蘭數
//Lucas定理實現C(n,m)%p的代碼:p為素數
LL exp_mod(LL a, LL b, LL p)
{ //快速冪取模
    LL res = 1;
    while(b != 0)
    {
        if(b&1) res = (res * a) % p;
        a = (a*a) % p;
        b >>= 1;
    }
    return res;
}
LL Comb(LL a, LL b, LL p)
{ //求組合數C(a,b)%p
    if(a < b)   return 0;
    if(a == b)  return 1;
    if(b > a - b)   b = a - b;
    LL ans = 1, ca = 1, cb = 1;
    for(LL i = 0; i < b; ++i)
    {
        ca = (ca * (a - i))%p;
        cb = (cb * (b - i))%p;
    }
    ans = (ca*exp_mod(cb, p - 2, p)) % p;
    return ans;
}
LL Lucas(LL n,LL m,LL p)
{ //Lucas定理求C(n,m)%p
     LL ans = 1;
     while(n&&m&&ans)
    {
        ans = (ans*Comb(n%p, m%p, p)) % p;
        n /= p;
        m /= p;
     }
     return ans;
}



五.遞推方程

1.線性遞推方程:

Fn-b1* Fn-1-b2* Fn-2-…-bk* Fn-k=0
其通項公式為:Fn=c1* q1^n +c2* q2^n +…+ck* qk^n
其中q1…qn是特征方程,q^k -b1* q^(k-1)- b2* q^(k-2)-…-bk=0 的根
c1…ck是常數,由初值決定

2.非線性遞推方程:

Fn-b1* Fn-1-b2* Fn-2-…-bk* Fn-k=S(n)
其通項公式為:Fn=c1* q1^n +c2 * q2^n +…+ck*qk^n+fn
其中q1…qn是特征方程的根,fn為一特解

3.解遞推方程:杜教BM模板,(黑科技)

給出前k項,即可求出第n項,只能用於符合遞推方程形式的方程

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <cassert>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
// head
int _;
ll n;
namespace linear_seq {
    const int N=10010;
    ll res[N],base[N],_c[N],_md[N];
    vector<int> Md;
    void mul(ll *a,ll *b,int k) {
        rep(i,0,k+k) _c[i]=0;
        rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
        for (int i=k+k-1;i>=k;i--) if (_c[i])
            rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
        rep(i,0,k) a[i]=_c[i];
    }
    int solve(ll n,VI a,VI b) { // a 系數 b 初值 b[n+1]=a[0]*b[n]+...
        // printf("%d\n",SZ(b));
        ll ans=0,pnt=0;
        int k=SZ(a);
        assert(SZ(a)==SZ(b));
        rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
        Md.clear();
        rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
        rep(i,0,k) res[i]=base[i]=0;
        res[0]=1;
        while ((1ll<<pnt)<=n) pnt++;
        for (int p=pnt;p>=0;p--) {
            mul(res,res,k);
            if ((n>>p)&1) {
                for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
                rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
            }
        }
        rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
        if (ans<0) ans+=mod;
        return ans;
    }
    VI BM(VI s) {
        VI C(1,1),B(1,1);
        int L=0,m=1,b=1;
        rep(n,0,SZ(s)) {
            ll d=0;
            rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
            if (d==0) ++m;
            else if (2*L<=n) {
                VI T=C;
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                L=n+1-L; B=T; b=d; m=1;
            } else {
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                ++m;
            }
        }
        return C;
    }
    int gao(VI a,ll n) {
        VI c=BM(a);
        c.erase(c.begin());
        rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
        return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
    }
};
int main() {
    for (scanf("%d",&_);_;_--) {
        scanf("%lld",&n);
        vector<int>a;
        a.push_back(1);
        a.push_back(3);
        a.push_back(5);
        a.push_back(7);
        printf("%d\n",linear_seq::gao(a,n-1));
    }
}


六.母函數:

http://www.wutianqi.com/?p=596
用於計算組合問題可能情況數

1.普通母函數:

普通母函數通常解決類似如下的問題:
給5張1元,4張2元,3張5元,要得到15元,有多少種組合?
某些時候會規定至少使用3張1元、1張2元、0張5元。
某些時候會規定有無數張1元、2元、5元。

const int MAX=10000;
const int INF=0x3f3f3f3f;
//為計算結果,b為中間結果。
// K對應具體問題中物品的種類數。
//v[i]表示該乘積表達式第i個因子的權重,對應於具體問題的每個物品的價值或者權重。
//n1[i]表示該乘積表達式第i個因子的起始系數,對應於具體問題中的每個物品的最少個數,即最少要取多少個。0
//n2[i]表示該乘積表達式第i個因子的終止系數,對應於具體問題中的每個物品的最多個數,即最多要取多少個。INF
//P為可能出現的最大指數(所求情況)
int a[MAX],b[MAX],P;
int k,v[MAX],n1[MAX],n2[MAX];
//初始化a
void cal(int n)       //n為種類數
{
    memset(a,0,sizeof(a));
    a[0]=1;
    for (int i=1;i<=n;i++)//循環每個因子
    {
        memset(b,0,sizeof(b));
        for (int j=n1[i];j<=n2[i]&&j*v[i]<=P;j++)//循環每個因子的每一項
            for (int k=0;k+j*v[i]<=P;k++)//循環a的每個項
                b[k+j*v[i]]+=a[k];//把結果加到對應位
        memcpy(a,b,sizeof(b));//b賦值給a
    }
}

2.指數母函數:

這里寫圖片描述
這里寫圖片描述
用於求多重排列數
如以下問題:
假設有8個元素,其中a1重復3次,a2重復2次,a3重復3次。從中取r個排列,求其排列數。
對於上面的問題“假設有8個元素,其中a1重復3次,a2重復2次,a3重復3次。從中取r個排列,求其排列數。”:

double c1[N],c2[N];
LL fac[N];
void Fac()            //求階乘
{
    fac[0]=1;
    for(int i=1;i<=N;i++)
        fac[i]=fac[i-1]*i;
}

int a[N];           //1~n每種的個數
void cal(int n,int m)          //有n種,取m個
{
    memset(c1,0,sizeof(c1));
    memset(c2,0,sizeof(c2));

    c1[0]=1.0/fac[0];
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
            for(int k=0;k+j<=m && k<=a[i];k++)
                c2[k+j]+=c1[j]/fac[k];
        for(int j=0;j<=m;j++)
            c1[j]=c2[j],c2[j]=0;
    }
}
ans=c1[m]*fac[m];           //取m個時的多重排列數

3.整數拆分:

1.有序拆分:把自然數n拆分成r個自然數之和,方案數有C(n-1,r-1)
2.無序拆分:把自然數n拆分成m個自然數之和,方案數有F(n,m)=F(n-1,m-1)+F(n-m,m),F(n,1)=F(n,n)=1

母函數法求無序拆分數
#include <iostream>
#include <string.h>
using namespace std;

const int MAX=10000;
const int INF=0x3f3f3f3f;
//為計算結果,b為中間結果。
// K對應具體問題中物品的種類數。
//v[i]表示該乘積表達式第i個因子的權重,對應於具體問題的每個物品的價值或者權重。
//n1[i]表示該乘積表達式第i個因子的起始系數,對應於具體問題中的每個物品的最少個數,即最少要取多少個。
//n2[i]表示該乘積表達式第i個因子的終止系數,對應於具體問題中的每個物品的最多個數,即最多要取多少個。
//P為可能出現的最大指數(所求情況)
int a[MAX],b[MAX],P;
int k,v[MAX],n1[MAX],n2[MAX];
//初始化a
void cal(int n)
{
    memset(a,0,sizeof(a));
    a[0]=1;
    for (int i=1;i<=n;i++)//循環每個因子
    {
        memset(b,0,sizeof(b));
        for (int j=n1[i];j<=n2[i]&&j*v[i]<=P;j++)//循環每個因子的每一項
            for (int k=0;k+j*v[i]<=P;k++)//循環a的每個項
                b[k+j*v[i]]+=a[k];//把結果加到對應位
        memcpy(a,b,sizeof(b));//b賦值給a
    }
}


int main()
{
    int n;
    memset(n1,0,sizeof(n1));
    memset(n2,INF,sizeof(n2));
    for(int i=0;i<=125;i++)
        v[i]=i;
    while(cin>>n)
    {
        P=n;
        cal(n);
        cout<<a[n]<<endl;
    }
    return 0;
}

七.Polya計數:

這里寫圖片描述

1.定義:

設G是有限集X上的置換群,點a,b∈X稱為"等價"的,當且僅當,存在π∈G使得π(a)=b,記為a~b,這種等價條件下,X的元素形成的等價類稱為G的軌道,它是集X的一個子集,G的任意兩個不同的軌道之交是空集,所以置換群G的軌道全體是集合X的一個划分,構成若干個等價類,等價類的個數記為L。

2.Zk (K不動置換類):

設G是1…n的置換群。若K是1…n中某個元素,G中使K保持不變的置換的全體,記以Zk,叫做G中使K保持不動的置換類,簡稱K不動置換類。

3.Ek(等價類):

設G是1…n的置換群。若K是1…n中某個元素,K在G作用下的軌跡,記作Ek。即K在G的作用下所能變化成的所有元素的集合。.
這個時候有:|Ek|*|Zk|=|G|成立(k=1,2,…n)。

4.C(π):

對於一個置換π∈G,及a∈X,若π(a)=a,則稱a為π的不動點。π的不動點的全體記為C(π)。例如π=(123)(3)(45)(6)(7),X={1,2,3,4,5,6,7};那么C(π)={3,6,7}共3個元素。

5.Burnside引理:

L=1/|G|* (Z1+Z2+Z3+Z4+…Zk)=1/|G|*(C(π1)+C(π2)+C(π3)+…+C(πn))(其中k∈X,π∈G)。

6.Polya定理:

設G={π1,π2,π3…πn}是X={a1,a2,a3…an}上一個置換群,用m中顏色對X中的元素進行塗色,那么不同的塗色方案數為:1/|G|*(m^C(π1)+ m^C(π2)+ m^C(π3) +…+m^C(πk)). 其中C(πk)為置換πk的循環節的個數。

通過Ploya定理求解方案數
#define N 100000
int prime[N];
bool is_prime[N];
 
int sieve(int n)
{
    int p = 0;
    for (int i = 0; i <= n; ++i) is_prime[i] = true;
    is_prime[0] = is_prime[1] = false;
    for (int i = 2; i <= n; ++i) {
        if (is_prime[i]) {
            prime[p++] = i;
            for (int j = 2 * i; j <= n; j += i)
                is_prime[j] = false;
        }
    }
    return p;
}
 
int phi(int n)
{
    int rea = n;
    for(int i = 0; prime[i] * prime[i] <= n; i++)
    {
        if(n % prime[i] == 0)
        {
            rea = rea - rea / prime[i];
            while (n % prime[i] == 0)  n /= prime[i];
        }
    }
    if(n > 1)
        rea = rea - rea / n;
    return rea;
}
 
ll polya(int m, int n)
{
    ll sum = 0;
    ll i;
    for (i = 1; i * i < n; ++i) {
        if (n % i == 0) {
            sum += phi(i) * pow(m, n / i);
            sum += phi(n / i) * pow(m, i);
        }
    }
    if (i * i == n) sum += phi(i) * pow(m, i);
    if (n & 1) sum += n * pow(m, n / 2 + 1);
    else sum += (pow(m, n / 2) + pow(m, n / 2 + 1)) * n / 2;
 
    return sum / 2 / n;
}

八.快速傅里葉變換(FFT):

https://blog.csdn.net/ggn_2015/article/details/68922404
用於計算多項式乘法O(nlogn)

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<complex>
#include <iostream>
using namespace std;
typedef complex<double> cd;//復數類的定義
const int maxl=2094153;//nlogn的最大長度(來自leo學長的博客)
const double PI=3.14159265358979;//圓周率,不解釋
cd a[maxl],b[maxl];//用於儲存變換的中間結果
int rev[maxl];//用於儲存二進制反轉的結果
void getrev(int bit){
    for(int i=0;i<(1<<bit);i++){//高位決定二進制數的大小
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
    }//能保證(x>>1)<x,滿足遞推性質
}
void fft(cd* a,int n,int dft){//變換主要過程
    for(int i=0;i<n;i++){//按照二進制反轉
        if(i<rev[i])//保證只把前面的數和后面的數交換,(否則數組會被翻回來)
            swap(a[i],a[rev[i]]);
    }
    for(int step=1;step<n;step<<=1){//枚舉步長的一半
        cd wn=exp(cd(0,dft*PI/step));//計算單位復根
        for(int j=0;j<n;j+=step<<1){//對於每一塊
            cd wnk(1,0);//!!每一塊都是一個獨立序列,都是以零次方位為起始的
            for(int k=j;k<j+step;k++){//蝴蝶操作處理這一塊
                cd x=a[k];
                cd y=wnk*a[k+step];
                a[k]=x+y;
                a[k+step]=x-y;
                wnk*=wn;//計算下一次的復根
            }
        }
    }
    if(dft==-1){//如果是反變換,則要將序列除以n
        for(int i=0;i<n;i++)
            a[i]/=n;
    }
}
int output[maxl];
char s1[maxl],s2[maxl];

void getmuti()    //計算高精度大數乘法,輸入兩個數a,b
{
    scanf("%s%s",s1,s2);//讀入兩個數
    int l1=strlen(s1),l2=strlen(s2);//就算"次數界"
    int bit=1,s=2;//s表示分割之前整塊的長度
    for(bit=1;(1<<bit)<l1+l2-1;bit++){
        s<<=1;//找到第一個二的整數次冪使得其可以容納這兩個數的乘積
    }
    for(int i=0;i<l1;i++){//第一個數裝入a
        a[i]=(double)(s1[l1-i-1]-'0');
    }
    for(int i=0;i<l2;i++){//第二個數裝入b
        b[i]=(double)(s2[l2-i-1]-'0');
    }
    getrev(bit);fft(a,s,1);fft(b,s,1);//dft
    for(int i=0;i<s;i++)a[i]*=b[i];//對應相乘
    fft(a,s,-1);//idft
    for(int i=0;i<s;i++){//還原成十進制數
        output[i]+=(int)(a[i].real()+0.5);//注意精度誤差
        output[i+1]+=output[i]/10;
        output[i]%=10;
    }
    int i;
    for(i=l1+l2;!output[i]&&i>=0;i--);//去掉前導零
    if(i==-1)printf("0");//特判長度為0的情況
    for(;i>=0;i--){//輸出這個十進制數
        printf("%d",output[i]);
    }
    putchar('\n');
}

void getpoly()    //計算多項式乘法
{
    int n,m;
    scanf("%d%d",&n,&m);    //輸入量多項式最高項次數
    for(int i=0;i<=n;i++) scanf("%lf",&a[i].real());    //讀入第一個多項式的系數(a0+a1*x+a2*x^2+a3*x^3+.....+an*x^n)
    for(int i=0;i<=m;i++) scanf("%lf",&b[i].real());    //讀入第二個多項式的系數(b0+b1*x+b2*x^2+b3*x^3+.....+bn*x^n)
    int bit=0,s=1;//s表示分割之前整塊的長度
    for(s=1;s<=n+m;s<<=1)
        bit++;
    getrev(bit);fft(a,s,1);fft(b,s,1);//dft
    for(int i=0;i<s;i++)a[i]*=b[i];//對應相乘
    fft(a,s,-1);//idft
    for(int i=0;i<=n+m;i++)
        printf("%d ",(int)(a[i].real()+0.5));   //表示乘完多項式各項的系數,(a0+a1*x+a2*x^3...)
}

int main(){
    getmuti();     //10*10=100
    getpoly();    //(1+2x)*(1+2x+x2)=1+4x+5x2+2x3
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM