总算编好了这一题,我表示200+行,亚历山大。

题目描述很简单,做起来不简单啊。(高精度的取模和除法不是一般的恶心!)

先说一下非高精度的一般做法。

求两个数a,b的最小公倍数,就是a、b的乘积与a、b的最大公因数的商。即:lcm(a,b)=a*b/gcd(a,b);

然而,如果a,b都是高精度数,我们不仅要算乘法和除法(看这个式子就知道了),还要有减法(因为mod运算中,c mod d=c-cdiv d*d)

求gcd(a,b)(最大公因数)一般有两种方法:辗转相除法(见http://baike.baidu.com/link?url=7yVbCI_TCmVcqMbEu_BgYb0ejvSJDZVmkw9g0UjrMU_3yaHAAFABgfjuk1ZT5rgI)和更相减损法(见http://baike.baidu.com/view/1340422.htm)。


-----------------------------------------------我是华丽的分界线----------------------------------------------------



一开始的时候,为了避免麻烦的除法和取模,我想用更相减损法(虽然辗转相除法快很多)。

更相减损法简介:a和b为操作数。(设a=48,b=30)

(1)把a和b的2的因数都约掉(不约也没事,但这样更快)。如:变成a=24,b=15。

(2)若a>b,a=a-b;若b>a,b=b-a;

(3)重复(2)的操作,直到a和b相等。

如: (24,15)->(9,15)->(9,6)->(3,6)->(3,3),即最大公因数为3*2=6。

 

-----------------------------------------------我是华丽的分界线-----------------------------------------------------

 

 我好不容易编完、调试好后,发现超时严重哪!

原来,假设有两个数a=1999,b=1。求他们的最大公因数时,按照更相减损法的步骤,会进行1999次上下!!

即:a=a-b=1999-1=1998;

       a=a-b=1998-1=1997;

      …………

那么我们可以设想,如果a=10000000000000,b=1,我的程序会怎么样?!



-----------------------------------------------我是华丽的分界线-----------------------------------------------------



思考再三,我决定编辗转相除法。

首先我们要研究如何编高效的除法运算。二分有很好的效果。

基本思路:设a/b,a的位数是k1位,b的位数k2位。那么我们的答案ans=erfen(l,r)。其中一开始时,l=10000……(共k2-k1位),r=99999……(共k2-k1+1位)。二分的思路就是,如果当前的mid*b>a,向左二分,否则向右二分。

取模运算和除法几乎一样,只是后来要减一下,即a mod b=a-ans*b。

其他原理差不多,为了提高效率,我也先除了2。



-----------------------------------------------我是华丽的分界线-----------------------------------------------------
代码一:更相减损法(只过了三个点)
#include<stdio.h>
#include<cstring>

using namespace std;
struct arr
{
  long num,p[101];
  arr() {num=0;memset(p,0,sizeof(p));}
}a,b,ans;
long i,tot;
char u;
bool w[10001];
void change()
{
  long i,t;
  for(i=1;i<=a.num/2;i++){t=a.p[i];a.p[i]=a.p[a.num-i+1];a.p[a.num-i+1]=t;}
  for(i=1;i<=b.num/2;i++){t=b.p[i];b.p[i]=b.p[b.num-i+1];b.p[b.num-i+1]=t;}
}
arr chen(arr a,arr b)
{
  long i,j;
  arr CHEN;
  for (i=1;i<=a.num;i++)
    for (j=1;j<=b.num;j++)
      CHEN.p[i+j-1]+=a.p[i]*b.p[j];
  CHEN.num=a.num+b.num-1;
  for (i=1;i<=CHEN.num;i++)
    if (CHEN.p[i]>9){CHEN.p[i+1]+=CHEN.p[i]/10;CHEN.p[i]%=10;}
  while (CHEN.p[CHEN.num+1]>0)
  {
    CHEN.num++;
    CHEN.p[CHEN.num+1]+=CHEN.p[CHEN.num]/10;
    CHEN.p[CHEN.num]%=10;
  }
  return CHEN;
}
arr add(arr a,arr b)
{
  long i,x=0;arr ADD;
  ADD.num=a.num;if (b.num>ADD.num)ADD.num=b.num;
  for (i=1;i<=ADD.num;i++)
    {
      ADD.p[i]=a.p[i]+b.p[i]+x;
      x=ADD.p[i]/10;ADD.p[i]%=10;
    }
   while (x>0)
   {
     ADD.num++;
     ADD.p[ADD.num]=x%10;
     x=x/10;
   }
  return ADD;
}
arr jian(arr a,arr b)
{
  long i=1,j,k;
  while (i<=b.num)
  {
    if (a.p[i]>=b.p[i])a.p[i]=a.p[i]-b.p[i];
    else
    {
      j=i+1;
      while (a.p[j]==0) j++;
      a.p[j]--;
      for (k=i+1;k<j;k++) a.p[k]=9;
      a.p[i]=a.p[i]+10-b.p[i];
    }
    i++;
  }
  while (a.p[a.num]==0) a.num--;
  return a;
}
long check(arr a,arr b)
{
  if (a.num>b.num) return 1;
  if (a.num<b.num) return -1;
  for (long i=a.num;i>0;i--)
  {
    if (a.p[i]>b.p[i]) return 1;
    else if (a.p[i]<b.p[i]) return -1;
  }
  return 0;
}
void kick()
{
  long x,i;
  while (a.p[1]%2==0&&b.p[1]%2==0)
  {
    x=0;
    for  (i=a.num;i>0;i--)
    {
      a.p[i]=(a.p[i]+x*10);
      x=a.p[i]%2;
      a.p[i]/=2;
    }
    x=0;
    for  (i=b.num;i>0;i--)
    {
      b.p[i]=(b.p[i]+x*10);
      x=b.p[i]%2;
      b.p[i]/=2;
    }
    tot++;
  }
}
void make()
{
  long temp;long i,j,k,flag=1,x;bool boo=true;
  while (0==0)
  {
    temp=check(a,b);
    if (temp==0) break;
    k++;if (temp==1)
    {
      w[k]=true;
      a=jian(a,b);
    }
    else
    {
      w[k]=false;
      b=jian(b,a);
    }
  }
  ans=a;
  for (i=2;i<=a.num;i++) a.p[i]=0;
  for (i=2;i<=b.num;i++) b.p[i]=0;
  a.p[1]=1;b.p[1]=1;a.num=1;b.num=1;
  for (i=k;i>0;i--)
  {
    if (w[i]) a=add(a,b);
    else b=add(a,b);
  }
  a=chen(a,b);
  ans=chen(a,ans);
  for (i=1;i<=tot;i++)
  {
    x=0;
    for (j=1;j<=ans.num;j++)
    {
      ans.p[j]=ans.p[j]*2+x;
      x=ans.p[j]/10;
      ans.p[j]%=10;
    }
    if (ans.p[ans.num+1]>0) ans.num++;
  }
}
int main()
{
  scanf("%c",&u);a.num=0;
  while (u!=' ')
  {
    a.num++;a.p[a.num]=u-48;
    scanf("%c",&u);
  }
  b.num=0;
  while (scanf("%c",&u)!=EOF)
  {
    b.num++;b.p[b.num]=u-48;
  }
  change();
  kick();
  make();
  for (i=ans.num;i>0;i--)
    printf("%ld",ans.p[i]);
  return 0;
}

代码二:辗转相除法(全过) 

#include<stdio.h>
#include<cstring>

using namespace std;
struct arr
{
  long num,p[301];
  arr() {num=1;memset(p,0,sizeof(p));}     //定义一个结构体,方便调用。ORZ 任轩笛的教导
}a,b,ans,plusone;
long i,tot;
char u;
void change()                                       //读进来是正的,处理是反的,倒一下。
{
  long i,t;
  for(i=1;i<=a.num/2;i++){t=a.p[i];a.p[i]=a.p[a.num-i+1];a.p[a.num-i+1]=t;}
  for(i=1;i<=b.num/2;i++){t=b.p[i];b.p[i]=b.p[b.num-i+1];b.p[b.num-i+1]=t;}
}
long check(arr a,arr b)                             //检验a和b的大小(高精度)
{
  if (a.num>b.num) return 1;
  if (a.num<b.num) return -1;
  for (long i=a.num;i>0;i--)
  {
    if (a.p[i]>b.p[i]) return1;
    else if (a.p[i]<b.p[i]) return-1;
  }
  return 0;
}
void kick()                                          //除掉因数2(可以没有此段)
{
  long x,i;
  while(a.p[1]%2==0&&b.p[1]%2==0)
  {
    x=0;
    for (i=a.num;i>0;i--)
    {
      a.p[i]=(a.p[i]+x*10);
      x=a.p[i]%2;
      a.p[i]/=2;
    }
    if (a.p[a.num]==0) a.num--;
    x=0;
    for (i=b.num;i>0;i--)
    {
      b.p[i]=(b.p[i]+x*10);
      x=b.p[i]%2;
      b.p[i]/=2;
    }
    if (a.p[a.num]==0) a.num--;
    tot++;                                                         //tot最多只有300+的
  }
}
//-------------------------------------分割线--------------------------------------
arr chen(arr a,arr b)                                     //基础的高精度乘法
{
  long i,j;
  arr CHEN;
  for (i=1;i<=a.num;i++)
    for (j=1;j<=b.num;j++)
     CHEN.p[i+j-1]+=a.p[i]*b.p[j];
  CHEN.num=a.num+b.num-1;
  for (i=1;i<=CHEN.num;i++)
    if (CHEN.p[i]>9){CHEN.p[i+1]+=CHEN.p[i]/10;CHEN.p[i]%=10;}
  while (CHEN.p[CHEN.num+1]>0)
  {
    CHEN.num++;
   CHEN.p[CHEN.num+1]+=CHEN.p[CHEN.num]/10;
    CHEN.p[CHEN.num]%=10;
  }
  return CHEN;
}
arr add(arr a,arr b)                                     //基础的高精度加法
{
  long i,x=0;arr ADD;
  ADD.num=a.num;if (b.num>ADD.num)ADD.num=b.num;
  for (i=1;i<=ADD.num;i++)
    {
     ADD.p[i]=a.p[i]+b.p[i]+x;
     x=ADD.p[i]/10;ADD.p[i]%=10;
    }
   while (x>0)
   {
     ADD.num++;
     ADD.p[ADD.num]=x%10;
     x=x/10;
   }
  return ADD;
}
arr jian(arr a,arr b)                                //基础的高精度减法
{
  long i=1,j,k;
  while (i<=b.num)
  {
    if (a.p[i]>=b.p[i])a.p[i]=a.p[i]-b.p[i];
    else
    {
      j=i+1;
      while (a.p[j]==0) j++;
      a.p[j]--;
      for (k=i+1;k<j;k++)a.p[k]=9;
     a.p[i]=a.p[i]+10-b.p[i];
    }
    i++;
  }
  while (a.p[a.num]==0&&a.num>1)a.num--;
  return a;
}
//-------------------------------------分割线--------------------------------------
arr CHU(arr a,arr b)                                //高精度除法(二分)
{
  arr l,r,mid,plusone;plusone.p[1]=1;longtemp=check(a,b),i,x;
  l.num=a.num-b.num;r.num=l.num+1;
  for (i=1;i<l.num;i++)l.p[i]=0;l.p[l.num]=1;
  for (i=1;i<=r.num;i++) r.p[i]=9;
  while (true)
  {
    mid=add(l,r);
    x=0;
    for (i=mid.num;i>1;i--)
    {
     mid.p[i]=(mid.p[i]+x*10);
      x=mid.p[i]%2;
      mid.p[i]/=2;
    }
    mid.p[1]+=x*10;if (mid.p[1]%2==1)mid.p[1]=mid.p[1]/2+1;else mid.p[1]/=2;
    if (mid.p[mid.num]==0)mid.num--;

    temp=check(a,chen(b,mid));
    if (temp==1) l=mid;
    if (temp==-1)r=jian(mid,plusone);
    if (temp==0||check(l,r)==0)break;
  }
  if (temp==0) return mid;else returnl;
}
arr MOD(arr a,arr b)                                      //高精度求余数
{
  arr l,r,mid,mod,plusone;plusone.p[1]=1;longtemp=check(a,b),i,x;
  l.num=a.num-b.num;r.num=l.num+1;
  if (l.num==0) l.num++;
  for (i=1;i<l.num;i++)l.p[i]=0;l.p[l.num]=1;
  for (i=1;i<=r.num;i++) r.p[i]=9;
  while (true)
  {
    mid=add(l,r);
    x=0;
    for (i=mid.num;i>1;i--)
    {
     mid.p[i]=(mid.p[i]+x*10);
      x=mid.p[i]%2;
      mid.p[i]/=2;
    }
    mid.p[1]+=x*10;if (mid.p[1]%2==1)mid.p[1]=mid.p[1]/2+1;else mid.p[1]/=2;
    if (mid.p[mid.num]==0)mid.num--;

    temp=check(a,chen(b,mid));
    if (temp==1) l=mid;
    if (temp==-1)r=jian(mid,plusone);
    if (temp==0||check(l,r)==0)break;
  }
  if (temp==0) mod=jian(a,chen(b,mid));elsemod=jian(a,chen(b,l));
  return mod;
}
arr gcd(arr a,arr b)                                     //辗转相除法
{
  arr mod=MOD(a,b);
  if (mod.num==1&&mod.p[1]==0) returnb;
  return gcd(b,mod);
}
void make()                                           //主要处理场地
{
  long i,j,x;
  if (check(a,b)==1) ans=gcd(a,b);elseans=gcd(b,a);            //求最大公因数
  ans=CHU(a,ans);                                                             //本来是ans=a*b/ans,为了优化,反了一下。
  ans=chen(ans,b);
  for (i=1;i<=tot;i++)                                                           //乘上2的个数
  {
    x=0;
    for (j=1;j<=ans.num;j++)
    {
      ans.p[j]=ans.p[j]*2+x;
      x=ans.p[j]/10;
      ans.p[j]%=10;
    }
    if (x>0)
     {
       ans.num++;
      ans.p[ans.num]=x;
     }
  }
}
//-------------------------------------分割线--------------------------------------
int main()
{
  scanf("%c",&u);a.num=0;                                //字符串读入处理操作
  while (u!=' ')
  {
    a.num++;a.p[a.num]=u-48;
    scanf("%c",&u);
  }
  b.num=0;
  while (scanf("%c",&u)!=EOF)
  {
    b.num++;b.p[b.num]=u-48;
  }
  change();
  kick();
  make();
  for (i=ans.num;i>0;i--)                                         //倒着输出
    printf("%ld",ans.p[i]);
  return 0;
}

vijos1047题解的更多相关文章

  1. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  2. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  3. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  4. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  5. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  6. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  8. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

  9. CF100965C题解..

    求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...

随机推荐

  1. 编写高质量的 Java 代码

    代码质量概述 代码质量所涉及的5个方面,编码标准.代码重复.代码覆盖率.依赖项分析.复杂度分析.这5方面很大程序上决定了一份代码的质量高低. 我们分别来看一下这5方面:编码标准:这个想必都很清楚,每个 ...

  2. 13.python笔记之pyyaml模块

    Date:2016-03-25 Title:13.Python笔记之Pyymal模块使用 Tags:Python Category:Python 博客地址:www.liuyao.me 作者:刘耀 YA ...

  3. [Sciter系列] MFC下的Sciter&ndash;1.创建工程框架

    Sciter SDK中提供的Win32下例程很多,唯独使用很多(对我个人而言)的MFC框架下Sciter程序的构建讲的很少,虽然MFC有这样那样的诟病,但是不可否认的是编写一般的小项目,这仍然是大多数 ...

  4. JQUERY1.9学习笔记 之基本过滤器(五) 大于选择器

    大于选择器:jQuery( ":gt(index)" )jQuery( ":gt(-index)" ) 例:大于TD5 到TD8 用黄色背景,TD8用红色文字. ...

  5. spring @Autowired和jdk的@Resource区别

    当一个接口只有一个实例时,使用这两个注解的效果是一样的. 当含有两个实例时,非得使用 @Autowired 那么定义的引用类型必须和service实现类定义的名字相同,参照下图 定义第一个servic ...

  6. 用SecureCRT来上传和下载文件

    用SSH管理linux服务器时经常需要远程与本地之间交互文件.而直接用SecureCRT自带的上传下载功能无疑是最方便的,SecureCRT下的文件传输协议有ASCII.Xmodem.Zmodem. ...

  7. 46个Linux面试常见问题送给你

    问题一: 绝对路径用什么符号表示?当前目录.上层目录用什么表示?主目录用什么表示? 切换目录用什么命令? 答案:绝对路径: 如/etc/init.d当前目录和上层目录: ./  ../主目录: ~/切 ...

  8. python、java读数据

    python从txt文档中读数据有个特别神奇的函数 可以把txt文档中的数据直接读取成python数组 java用Scanner类读数据比较方便

  9. static, const

    static 静态的,类的静态成员函数,静态成员变量是和类相关的,但不和具体对象相关.即使没有具体对象,也能调用类的静态成员函数和成员变量.一般类的静态函数就是一个全局函数,只是作用域在包含它的文件中 ...

  10. FFmpeg简易播放器的实现-音频播放

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10068490.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...