您的位置:首页 > 其它

Uva 10375 Choose and divide 素数唯一分解定理

2015-09-15 16:21 429 查看
Description

The binomial coefficient C(m, n) is defined as C(m, n) = m!/((m − n)! n!) Given four natural numbers p, q, r, and s, compute the the result of dividing C(p, q) by C(r, s).

Input

Input consists of a sequence of lines. Each line contains four non-negative integer numbers giving values for p, q, r, and s, respectively, separated by a single space. All the numbers will be smaller than 10,000 with p ≥ q and r ≥ s.

Output

For each line of input, print a single line containing a real number with 5 digits of precision in the fraction,giving the number as described above. You may assume the result is not greater than 100,000,000.

Sample Input

10 5 14 9

93 45 84 59

145 95 143 92

995 487 996 488

2000 1000 1999 999

9998 4999 9996 4998

Sample Output

0.12587

505606.46055

1.28223

0.48996

2.00000

3.99960

题意极其粗暴,就是求组合数C(p,q)/C(r,s)的值,保留五位小数。当然难度在于,四个系数p,q,r,s是10000以内。组合数这玩意啊。。稍微大一点这规模就疯长了,谁叫人家是用阶乘定义的呢?

这就用到了素数唯一分解定理。

素数唯一分解定理:合数p能且仅能有一种方式写成如下形式:

p=(p1)^(e1)*(p2)^(e2)*(p3)^(e3)*...*(pn)^(en)

其中,p1,p2,...pn是小于p的质数,并且p1<p2<p3<...<pn,e1,e2,e3...en是正整数指数。

简单化简得:



我们可以想到,将p,s,r-s,r,q,p-q都作质数分解,那么分子分母都可以写成10000以内的质数分解式,这就可以直接操作指数了。声明一个数组e,保存每个素数在式子中对应的指数。将最终的指数得到之后,就可以算最终结果了。

实现这一点,要事先得到10000以内的所有素数(这个题的规模在10000以内),这个题里面可以用最粗暴的方法得到,229MS通过

其中get和solve函数是唯一分解定理的实现

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<string>
#include<cmath>
#include<stack>
//#define file
#define maxn 100010
#define inf 0x3f3f3f3f
#define LL long long
using namespace std;
int p,q,r,s;
double ans;
int fac[10010];
vector<int>prime;
bool check(int n)
{
for(int i=2;i<n;i++)
{
if(n%i==0)
return false;
}
return true;
}
void init()
{
prime.push_back(2);
for(int i=3;i<=10000;i++)
{
if(check(i))
{
prime.push_back(i);
}
}
}
void get(int p,int q)
{
for(int i=0;i<prime.size();i++)
{
while(p%prime[i]==0)
{
p/=prime[i];
fac[i]+=q;
}
if(p==1)break;
}
}
void solve(int p,int q)
{
for(int i=1;i<=p;i++)
{
get(i,q);
}
}

int main()
{
init();
while(cin>>p>>q>>r>>s)
{
memset(fac,0,sizeof(fac));
solve(p,1);
solve(s,1);
solve(r-s,1);
solve(r,-1);
solve(q,-1);
solve(p-q,-1);
ans=1;
for(int i=0;i<prime.size();i++)
{
ans*=pow(prime[i],fac[i]);
}
printf("%.5f\n",ans);
}
}


然后我总觉得粗暴求素数时间开销略大,于是写了个筛法来作预处理

init1和init2函数是筛法实现

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<string>
#include<cmath>
#include<stack>
//#define file
#define maxn 100010
#define inf 0x3f3f3f3f
#define LL long long
using namespace std;
int p,q,r,s;
double ans;
int fac[10010];
int vis[10010];
vector<int>prime;
void init1()
{
int m=sqrt(10000+0.5);
memset(vis,0,sizeof(vis));
for(int i=2;i<=m;i++)
{
if(!vis[i])
{
for(int j=i*i;j<=10000;j+=i)
vis[j]=1;
}
}
}

void init2()
{
init1();
for(int i=2;i<=10000;i++)
{
if(!vis[i])
prime.push_back(i);
}
}
void get(int p,int q)
{
for(int i=0;i<prime.size();i++)
{
while(p%prime[i]==0)
{
p/=prime[i];
fac[i]+=q;
}
if(p==1)break;
}
}
void solve(int p,int q)
{
for(int i=1;i<=p;i++)
{
get(i,q);
}
}

int main()
{
init2();
while(cin>>p>>q>>r>>s)
{
memset(fac,0,sizeof(fac));
solve(p,1);
solve(s,1);
solve(r-s,1);
solve(r,-1);
solve(q,-1);
solve(p-q,-1);
ans=1;
for(int i=0;i<prime.size();i++)
{
ans*=pow(prime[i],fac[i]);
}
printf("%.5f\n",ans);
}
}


结果。。。。216MS通过。。这tmd也没怎么优化啊。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: