UVA10375 选择与除法 Choose and divide 题解

题目链接:

https://www.luogu.org/problemnew/show/UVA10375

分析:

这道题可以用唯一分解定理来做。

什么是唯一分解定理?百度即可,这里也简介一下。

对于任意一个自然数,都可以写成一些素数的幂次相乘的结果

比如说,26=13226=13*2,30=23530=2*3*5.

然后说详细做法:

首先make一个素数表prime,具体怎么做呢?

先用一个模板筛出合数:

for(int i=2;i<=100;i++)
{
	if(vis[i]!=1)
		for(int j=i*i;j<=10000;j+=i)
		vis[j]=1;
}
反正蒟蒻孤陋寡闻,这已经是我知道最快的造表法了

弄出了合数,我们再把每一个素数记到一个vector里

for(int i=2;i<=10000;i++)
	{
		if(vis[i]==0)
		{
			prime.push_back(i);
		}
	}	

这样为了之后循环幂次方便(一次完成,胜造多组数据

之后就套公式

C(m,n)=n!(mn)!m!C(m,n)=^{m!}_{n!(m-n)!}

(中间的除号被吞了
用唯一分解来表示每个数,方便约分,因为此题的实质就是解决越界问题。

EndEnd

代码:

#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
using namespace std;
int vis[10005];
vector<int>prime;
int e[10005];
void search(int n,int d)
{
	for(int i=0;i<prime.size();i++)
	{
		while(n%prime[i]==0)
		{
			n=n/prime[i];
			e[i]+=d;
		}
		if(n==1)break;
	}
}
void pd(int n,int d)
{
	for(int i=1;i<=n;i++)
	{
		search(i,d);
	}
}
int main()
{
	for(int i=2;i<=100;i++)
	{
		if(vis[i]!=1)
		for(int j=i*i;j<=10000;j+=i)
		vis[j]=1;
	}
	for(int i=2;i<=10000;i++)
	{
		if(vis[i]==0)
		{
			prime.push_back(i);
		}
	}	
	int p,q,r,s;
	while(scanf("%d%d%d%d",&p,&q,&r,&s)==4)
	{
		memset(e,0,sizeof(e));
		pd(p,1);
		pd(q,-1);
		pd(p-q,-1);
		pd(r,-1);
		pd(s,1);
		pd(r-s,1);
		double ans=1;
		for(int i=0;i<prime.size();i++)
		{
			ans*=pow(prime[i],e[i]);
		} 
		printf("%.5lf\n",ans);
	}
	return 0;
} 
posted @ 2019-02-25 21:44  ShineEternal  阅读(133)  评论(0编辑  收藏  举报