zoukankan      html  css  js  c++  java
  • 数论-质因数(gcd) UVa 10791

    https://vjudge.net/problem/UVA-10791/origin

    以上为题目来源Google翻译得到的题意:

    一组整数的LCM(最小公倍数)定义为最小数,即

    该集合的所有整数的倍数。有趣的是,可以表示任何正整数
    作为一组正整数的LCM。例如12可以表示为1、12或
    12、12或3、4或4、6或1、2、3、4等


    在此问题中,您将得到一个正整数
    N.您必须找出一组至少两个正整数,其LCM为N。
    如果可能,您必须选择元素总和最小的序列。我们会很高兴
    如果您仅打印此元素的总和
    组。因此,对于N = 12,您应该将4 + 3 = 7打印为
    4和3的LCM为12,而7则为最小值
    总结。

    输入值

    输入文件最多包含100个测试用例。每个测试用例都由一个正整数N(1≤N≤2^ 31 − 1)组成。
    输入在N = 0的情况下终止。
    案件不予处理。最多可以有
    100个测试用例。

    输出量

    每个测试用例的输出应由以“ Case#:”开头的一行组成,其中#是测试用例编号。它后面应该是问题陈述中指定的总和。看着那(这

    输出为样本输入以获取详细信息。

    翻译有一点语法问题,但是还是很好理解的。总结一下题目意思,就是给你一个N,你找到一组an使得n大于等于2. 且N 为an的最小公倍数,令an求和最小。

    思路:

    这道题最先要解决的就是,令求和最小这个要求,其实是什么?

    再进一步解释就是,N可以拆解成多个因数连乘,那么到底是因数越多,越小越好,还是因数越少,越大越好?

    很容易发现,如果是大的因数设为x,假设分解不彻底,那必有更小的bn使得bn之积等于x。

    考虑到直接思考多个情况显然比较麻烦,以及刚刚说的x的事情,我们发现,多个因数组合成大因数会先从C(2,n)开始,那么不妨就先讨论,如果由一项变成拆成两项会怎么样?

    即若令x=c*d,c+d大还是x大,即讨论   a+b与a*b大小的条件,

    先给出一般应试办法,如果用数去试,试多几次,你会发现除了质数和1以外,a+b永远小于a*b,

    那真的要计算证明有什么办法呢?以下是一些可能的思路(我自己没试过)和本人的办法(不一定很严谨)。

    • 函数思路,令F(a,b)=a*b-(a+b),讨论二元函数F的零点,或令F(a,b)=(a+b)/ab -1,一样是讨论零点,
    • 利用整数约束,解方程思路,令a+b=a*b,解该不定方程的正整数解,显然发现这是一个类似于,已知ax+by=c,“求逆元”的问题,即扩展欧几里得
    • 利用不等式,求a+b与a*b大小关系
    • 我的办法,虽然已经拆成了a,b两个,但是两个都还是动的,还是很麻烦呢
    • 那就干脆就再人为约束一下,令a不动b动,就有y1(b)=a*b,y2(b)=a+b,(a视为常数,大于等于1),比较俩函数在yOb图,交点出现的条件
      • 动手画图,解交点,很快发现,b=a/(a-1)为解。
      • 当a大于等于1,这个解显然小于等于2,且从这个解之后,有y1图像恒在y2上方
      • 得证,a+b小于等于a*b,若ab都不为1,取等条件是a=b且a=b=2
      • 什么时候会是1,当且仅当质数、

    好了,解完一个条件。但是完了吗?我一开始也以为完了,然后把N分解到能分解的极限,结果老WA,怎么回事?

    看题才发现还没完,又有另一个问题,它要求这一组数的最小公倍数是N,那显然,N不能被分解到极限,那分解的极限在哪里呢?

    我们要求分解的越多越好,N=Π(cn)^pn;(这里没有考虑分解项数一样的时候)

    试一试,发现an都为某一质数的次方时似乎可以达到,个数最多,且刚好因为 ai^pi,aj ^pj 互相只有唯一且各不相同质因子,任意两两互质,所以N必为最大公约数

    怎么证明?

    如果只有两个数,a,b,此时LCM(a,b)=a*b/gcd(a,b),那么如果有n个数,LCM(an)=Πan/gcd(an),又,N=Πan;

    (当且仅当,gcd(an)=1,即任意an都没有相同质因数的时候,才行,此时,当且仅当任意an都只有唯一质因子--即an为唯一质数的次方数的时候才满足,)(WA)

    然后。。。在大佬的指正下,这个证明办法是有问题的。an是唯一质因数并不是唯一满足gcd(an)= 1 的条件,如果an取出的abcd令任意gcd(a,b)!=gcd(b,c),也可以

    主要是这两种情况该怎么考虑, 即怎么理解分解程度--a+b小于a*b怎么往上推

    • (a*1, b^2,  c*1 ) (a*b,b*c,1*1)——(a*b,b*c),(a*c,b^2,1*1)——(a*c,b^2
    •   (a^2,b^2,c^2), (a*c^2 , b^2 , a )和 (  a*c,  b,    a*b)  ,   分解的项数一样,

    彻底分解肯定得到的是质数,那,质数,有约束N大小,那显然可以先打表,再试除

    只是又有一个前提,质数表打到N开根+1(2^16就可--65536)就行了,因为如果这些都搞不定N,那N一定筛不掉。

    不过,必须注意这个办法筛出的素数并不能覆盖所有的输入值N,素数表是不完全的,

    因此

    1. 要把没有被筛到的新素数另外检查,
    2. 或者筛到后面只剩下一个大于一但是又不在质数表的数加回去

    而且小心N的输出值 可能会是 long long

    另外要注意输入1时的条件,

    还有之前an本身就是,唯一质因数次方的时候,这里又WA了好多次orz我真是太菜了

    不含筛数:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <algorithm>
     4 #include <cmath>
     5 #include <cstring>
     6 #include <string>
     7 #include <limits.h>
     8 using namespace std;
     9 typedef long long ll;
    10 const int N=65536;
    11 const int M=1e5;
    12 bool x[M];//
    13 int p[M];
    14 int g=0; //记录素数值
    15 /*
    16 void getprime1(int n) {//埃式筛法
    17     memset(x,0,sizeof(x));
    18     memset(p,0,sizeof(p));
    19     g=0;//记录第几个素数--总数
    20     //x[2]=0;
    21     for(int i=2; i<=n; i++) {
    22         if(x[i]==0) {
    23             p[++g]=i;
    24             //printf("%5d ",i);
    25             for(int j=2; j*i<=n; j++) {
    26                 x[i*j]=1;
    27             }
    28         }
    29     }
    30 }
    31 void getprime2(int n) {//Ola
    32     memset(x,0,sizeof(x));
    33     memset(p,0,sizeof(p));
    34     g=0;//记录第几个素数--总数
    35     //x[2]=0;
    36     x[1]=0;//这题补一个要求
    37     for(int i=2; i<=n; i++) {
    38         if(x[i]==0) {
    39             g++;
    40             p[g]=i;
    41         }
    42         int t;
    43         for(int j=1; j<=g&&(t=i*p[j])<=n; j++) {
    44             x[t]=1;
    45         }
    46     }
    47 }
    48 */
    49 
    50 ll m=0;
    51 int main () {
    52 
    53     //getprime2(N);
    54     int l=0;
    55     while(~scanf("%lld",&m)&&m) {
    56         l++;
    57 
    58         //x[m]是不能直接用的,这点非常重要!!!!!
    59         if(m==1) {//否则会RE
    60             printf("Case %d: 2
    ",l);
    61         } else {
    62             //检查是不是没被覆盖的质数
    63             //或者恰好是唯一质因数的次方数
    64             int f=0;
    65             ll m1=m;
    66             ll t=0;
    67             for(ll i=2; i*i<=m1; i++) {//从2开始不然会一直m%1==0
    68                 ll q=1;
    69                 while(m%i==0) {
    70                     m/=i;
    71                     q*=i;
    72                 }
    73                 if(q>1) {
    74                     //printf("%d: %lld
    ",i,q);
    75                     t+=q;
    76                     f++;
    77                     //printf("%d
    ",q);
    78                 }
    79 
    80             }
    81             if(m!=1) {
    82                 t+=m,f++;
    83             }
    84             if(f==1)t++;
    85             printf("Case %d: %lld
    ",l,t);
    86         }
    87     }
    88     //printf("
    
    %d
    ",g);
    89 
    90     return 0;
    91 }
    View Code

    含筛数

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <algorithm>
     4 #include <cmath>
     5 #include <cstring>
     6 #include <string>
     7 #include <limits.h>
     8 using namespace std;
     9 typedef long long ll;
    10 const int N=100000;
    11 const int M=1e5;
    12 bool x[M];//
    13 int p[M];
    14 int g=0; //记录素数值
    15 /*
    16 void getprime1(int n) {//埃式筛法
    17     memset(x,0,sizeof(x));
    18     memset(p,0,sizeof(p));
    19     g=0;//记录第几个素数--总数
    20     //x[2]=0;
    21     for(int i=2; i<=n; i++) {
    22         if(x[i]==0) {
    23             p[++g]=i;
    24             //printf("%5d ",i);
    25             for(int j=2; j*i<=n; j++) {
    26                 x[i*j]=1;
    27             }
    28         }
    29     }
    30 }*/
    31 void getprime2(int n) {//Ola
    32     memset(x,0,sizeof(x));
    33     memset(p,0,sizeof(p));
    34     g=0;//记录第几个素数--总数
    35     //x[2]=0;
    36     x[1]=0;//这题补一个要求
    37     for(int i=2; i<=n; i++) {
    38         if(x[i]==0) {
    39             g++;
    40             p[g]=i;
    41         }
    42         int t;
    43         for(int j=1; j<=g&&(t=i*p[j])<=n; j++) {
    44             x[t]=1;
    45         }
    46     }
    47 }
    48 
    49 
    50 ll m=0;
    51 int main () {
    52 
    53     getprime2(N);
    54     int l=0;
    55     while(~scanf("%lld",&m)&&m) {
    56         l++;
    57         ll t=0;
    58         //x[m]是不能直接用的,这点非常重要!!!!!
    59         if(m==1||(m<N&&x[m]==0)) {//否则会RE
    60             m++;
    61             printf("Case %d: %lld
    ",l,m);
    62         } else {
    63             //检查是不是没被覆盖的质数
    64             int f=0;
    65             ll m1=m;//!!!
    66             for(int i=1; i<=g; i++) {
    67                 ll q=1;
    68                 while(m%p[i]==0) {
    69                     m/=p[i];
    70                     q*=p[i];
    71                 }
    72                 if(q>1) {
    73                     t+=q;
    74                     f++;
    75                     //
    76                     //printf("%d: %lld
    ",p[i],q);一直WA是因为没有把除完的数加上,
    77                     //如果除到最后都还大于1,说明不能被素数表上的完全筛完,
    78                     //最后剩下的一定是没有被筛完的质数,要加回去
    79                     //printf("%d
    ",q);
    80                 }
    81             }
    82             //最后剩下的一定是没有被筛完的质数,要加回去
    83             if(m!=1) {
    84                 t+=m;
    85             }
    86             if(f>1) {//不是质数
    87                 //printf("Case# %d: %lld
    ",l,t);WA半天发现没有#
    88                 printf("Case %d: %lld
    ",l,t);
    89             } else {//是质数
    90                 t++;
    91                 printf("Case %d: %lld
    ",l,t);
    92             }
    93 
    94         }
    95     }
    96     //printf("
    
    %d
    ",g);
    97 
    98     return 0;
    99 }
    View Code

    //结果发现不筛数更快2333

    害!

     (待续)

    老实一点,可爱多了
  • 相关阅读:
    JSON介绍
    json例子(后台取消息)
    在Struts 2中使用JSON Ajax支持
    JSON介绍
    json例子(后台取消息)
    64位播放器播放RMVB时一卡一顿
    标记一个:HookQQ QQFun CWUB
    Android开发环境搭建全程演示(jdk+eclip+android sdk)
    64位播放器播放RMVB时一卡一顿
    一种可做特殊用途的字符串匹配算法
  • 原文地址:https://www.cnblogs.com/KID-yln/p/12490307.html
Copyright © 2011-2022 走看看