POJ 1091 跳蚤 数论-容斥原理、扩展欧几里得

来源:互联网 发布:百度数据研究中心 编辑:程序博客网 时间:2024/06/10 05:33

POJ 1091 跳蚤 数论-容斥原理、扩展欧几里得

分类: 数论 250人阅读 评论(0) 收藏 举报

题目地址: http://poj.org/problem?id=1091


【题目大意】

输入整数n和m,找n个数字(都小于等于m),加上m共n+1个数字,使得这个n+1个数字满足存在x1…xn+1使得下列方程成立:a1*x1+a2*x2+a3*x3+…an*xn+m*xn+1=1,问共有多少组这样n+1个数字。

【提交情况】

Wa三次,开始用的math头文件中提供的pow(),经过double和long long的多次相互转化,丢失精度,导致无法通过。后自己写了个计算long long的次方的函数即可通过。

【解题思路】

根据扩展欧几里得,可以得知若想要a1*x1+a2*x2+a3*x3+…an*xn+m*xn+1=k成立,那么需要a1 a2 ….an m 的最大公约数是k,两边都除以k得到的就是我们想要求的那个方程。其实最大公约数另一个定义就是满足存在x1、x2……xn+1使得a1*x1+a2*x2+a3*x3+…an*xn+m*xn+1=k成立的最小整数,也就是说这是充要条件,其实题目就是问a1 a2……an、m共有多少组这样的组合使得最大公约数是1。

实际上就是求有多少组这样的数互质,由于m固定,所以只需要找到前n个数字不都含有m的某个质因子,有多少种组合即可。

先将m分解质因子,得到以后根据容斥原理进行计算:去掉都含其中一个质因子的,假设质因子为x,则一个位置的数去掉含x的质因子的,其实根数为m/x,然后加上含两个的……经过计算即可得到结果。


AC代码:

[cpp] view plaincopyprint?
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstdlib>  
  4. #include <cmath>  
  5. #include <cstring>  
  6. #include <string>  
  7. #include <vector>  
  8. #include <list>  
  9. #include <deque>  
  10. #include <queue>  
  11. #include <iterator>  
  12. #include <stack>  
  13. #include <map>  
  14. #include <set>  
  15. #include <algorithm>  
  16. #include <cctype>  
  17. #include <cfloat>  
  18. using namespace std;  
  19.   
  20. typedef long long LL;  
  21. const int N=110005;  
  22. const LL II=1000000007;  
  23. const int INF=0x3f3f3f3f;  
  24. const double PI=acos(-1.0);  
  25.   
  26. LL n,m,numi,temp,xh[N],pri[N];  
  27.   
  28. LL lpow(LL a,LL b)  
  29. {  
  30.     LL ans=1;  
  31.     while(b--)  
  32.         ans*=a;  
  33.     return ans;  
  34. }  
  35.   
  36. void gettemp(LL bt,LL now,LL top)  
  37. {  
  38.     LL get=m,i;  
  39.     if(now==top)  
  40.     {  
  41.         for(i=1;i<top;i++)  
  42.             get/=xh[i];  
  43.         temp+=lpow(get,n);  
  44.     }  
  45.     else  
  46.     {  
  47.         for(i=bt;i<=numi;i++)  
  48.         {  
  49.             xh[now]=pri[i];  
  50.             gettemp(i+1,now+1,top);  
  51.         }  
  52.     }  
  53. }  
  54.   
  55. LL xiaohao()  
  56. {  
  57.     LL i,j,p=m;  
  58.     numi=0;  
  59.     for(i=2;i*i<=p;i++)  
  60.     {  
  61.         if(p%i==0)  
  62.         {  
  63.             pri[++numi]=i;  
  64.             while(p%i==0)  
  65.                 p/=i;  
  66.         }  
  67.     }  
  68.     if(p>1)  
  69.         pri[++numi]=p;  
  70.     LL ans=lpow(m,n);//开始总共有m^n种  
  71.     for(i=1;i<=numi;i++)  
  72.     {  
  73.         temp=0;  
  74.         gettemp(1,1,i+1);  
  75.         if(i&1)  
  76.             ans-=temp;//去掉含奇数个质因子的  
  77.         else  
  78.             ans+=temp;//加上含偶数个质因子的  
  79.     }  
  80.     return ans;  
  81. }  
  82.   
  83. int main()  
  84. {  
  85.     LL i,j;  
  86.     while(~scanf("%lld%lld",&n,&m))  
  87.     {  
  88.         printf("%lld\n",xiaohao());  
  89.     }  
  90.     return 0;  
  91. }  





该题的推广与证明:

设有n个自然数,分别为a1, a2, a3 ....an,设其最大公约数为d, 则对于任意整数k1, k2, k3....kn(其最大公约数为e),可以得出c = k1*a1 + k2 * a2 + k3 * a3 ...kn * an的最小自然数为d*e。
下面只证明k1, k2, ...kn最大公约数为1和a1, a2, ....an最大公约数为1的情况。如果该情况得证,其它情况通过提取最大公约数可以转化为该情况。
首先证明n=2时候的情况,即c = k1*a1 + k2*a2的情况:
设a2 > a1且 a2 / a1 = p 余q,有:
c = k1*a1 + k2*a2                                       1
c = k1 * a1 + k2 * (p*a1 + q)                      2
c = (k1+k2*p) *a1 + k2 * q                         3
c = k1' * a1 + k2 * q                                    4
k1' 与k2的最大公约数依然为1, 而通过1式到4式的转换可以看出可以把较大的一个因数转化为该因数与另外一个因数的模,即上式中的把a2转化为q。
而我们都知道,这样不断模的结果就是a1和a2的最大公约数,所以c可以最后转化为:
c = k1'' * 1 + k2'' * 1
得证。
对于n = 3个的情况,即c= k1 * a1 + k2 * a2 + k3 * a3的情况,设a1, a2 的最大公约数为z, 则z 与a3的最大公约数必为1.
前两个提取z后,可以转化为n = 2时假的情况,故得证。

0 0