1. 程式人生 > >UVa10375(唯一分解定理)

UVa10375(唯一分解定理)

例題10-3 選擇與除法(Choose and Divide, UVa10375)
已知C(m,n) = m!/(n!(m-n)!),輸入整數p, q, r, s(p≥q,r≥s,p,q,r,s≤10000),計 算C(p,q)/C(r,s)。輸出保證不超過108,保留5位小數。
【分析】
本題正是唯一分解定理的用武之地。組合數C(m,n)的性質將在10.2.1節中介紹,本題只 需要用到它的定義。
首先,求出10000以內的所有素數primes,然後用陣列e表示當前結果的唯一分解式中各 個素數的指數。例如,e={1,0,2,0,0,0,…}表示2^1*5^2=50。

唯一分解:任何一個大於1的數n,可分解為若干素數相乘的形式。

以上摘自劉汝佳前輩紫書。

【程式碼】:

#include<bits/stdc++.h>
using namespace std;
int pri[15100],top,t[15100];
bool vis[15100];
void prime()
{
    memset(vis,1,sizeof(vis));
    top=0;
    for(int i=2;i<11000;i++)
        if(vis[i])
        {
            pri[top++]=i;
            for(int j=i+i;j<11000;j+=i)vis[j]=0;
        }
}
void get(int n,int flag)//將n的階乘唯一分解,存入t[]
{
    for(int i=n;i>1;i--)
    {
        int k=i;
        for(int j=0;j<top&&k>1;j++)
            while(k>1&&k%pri[j]==0){
                t[j]+=flag;k/=pri[j];
            }
    }
}
int main()
{
    prime();
    int p,q,r,s;
    while(cin>>p>>q>>r>>s)
    {
        memset(t,0,sizeof(t));
        get(p,1);
        get(s,1);
        get(r-s,1);
        get(q,-1);
        get(p-q,-1);
        get(r,-1);
        /*for(int i=0;i<10;i++)
            printf("%d ",t[i]);
        puts("");*/
        double ans=1;
        for(int i=0;i<top;i++)
        {
            ans*=pow(pri[i],t[i]);
        }
        printf("%.5lf\n",ans);
    }
}

另外我之前用了對數優化的方式,沒過。不知道是不是精度損失的問題,附程式碼,還請大佬指教:

#include<bits/stdc++.h>
using namespace std;
double fac[10109];
int main()
{
    fac[0]=log(1);
    for(int i=1;i<10101;i++)
        fac[i]=log(1.0*i)+fac[i-1];
    int p,q,r,s;
    while(cin>>p>>q>>r>>s)
    {
        double y=fac[p]+fac[s]+fac[r-s]-fac[q]-fac[p-q]-fac[r];
        printf("%.5lf\n",exp(y));
    }
}