Choose and divide(唯一分解定理)
首先说一下什么是唯一分解定理
唯一分解定理:任何一个大于1的自然数N,如果N不是质数,那么N可以分解成有限个素数的乘积;例:N=(p1^a1)*(p2^a2)*(p3^a3)......其中p1<p2<p3......
所以当我们求两个很大的数相除时 唯一分解定理是一个不错的选择,不会爆范围
下面具体说一下怎么求唯一分解定理:
首先我们需要知道所有的素数: 用埃式算法打表求得:
void is_prime() { cnt=0; for(int i=2;i<=maxn;i++) { if(!vis[i]) { prime[cnt++]=i; for(int j=i*2;j<=maxn;j+=i) vis[j]=true; } } }
接下来 就是求pi和ai了,
void solve(ll n,ll d) { for(int i=0;i<cnt;i++) { while(n%prime[i]==0) { n/=prime[i]; e[i]+=d; } if(n==1) return ; } }
下面看一道例题:
题目链接:https://vjudge.net/problem/UVA-10375
题目大意:用C(p,q)/C(r,s) 最后结果保留5位小数
思路:这道题不用唯一分解定理就不好做了,阶层相乘很有可能会爆数据范围,总之挺麻烦的,但是用唯一分解定理跟快就能A了 很简单 看代码应该就能明白了
#include<iostream> #include<stdio.h> #include<string.h> #include<math.h> using namespace std; typedef long long ll; const int maxn=1e4+5; ll prime[maxn]; ll e[maxn]; bool vis[maxn]; ll cnt; void is_prime() { cnt=0; for(int i=2;i<=maxn;i++) { if(!vis[i]) { prime[cnt++]=i; for(int j=i*2;j<=maxn;j+=i) vis[j]=true; } } } void solve(ll n,ll d)//n为相乘的数 当为分子时d为1 为分母时d为-1 很好理解 对应指数+1或-1嘛 { for(int i=0;i<cnt;i++) { while(n%prime[i]==0) { n/=prime[i]; e[i]+=d; } if(n==1) return ; } } int main() { ll p,q,r,s; double ans; memset(vis,false,sizeof(vis)); is_prime(); // for(int i=0;i<cnt;i++) cout<<prime[i]<<" "; // cout<<endl; while(cin>>p>>q>>r>>s) { ans=1; memset(e,0,sizeof(e)); for(int i=p;i>=1;i--) solve(i,1); for(int i=p-q;i>=1;i--) solve(i,-1); for(int i=q;i>=1;i--) solve(i,-1); for(int i=r-s;i>=1;i--) solve(i,1); for(int i=s;i>=1;i--) solve(i,1); for(int i=r;i>=1;i--) solve(i,-1); for(int i=0;i<cnt;i++) if(e[i]) ans*=pow(prime[i],e[i]); printf("%.5lf\n",ans); } }
当初的梦想实现了吗,事到如今只好放弃吗~