东北林业大学acm培训大一(四)(gcd与lcm)

1.基础知识点

2.取模运算的运算规则
(a + b) % p = (a % p + b % p) % p
(a - b) % p = (a % p - b % p) % p
(a * b) % p = (a % p * b % p) % p
a ^ b % p = ((a % p)^b) % p 【a的b次方】
3.欧几里得算法又叫辗转相除法,用来求得两个数的最大公约数,记作gcd(a,b)
其原理如下:

4.那么,怎么用程序实现欧几里得算法?
实际上我们可以写两种形式的辗转相除:递归和非递归。
非递归的模板: 递归的模板:
5.

  1. Lcm为最小公倍数
    当然我们求最小公倍数也是通过先求最大公约数得到的。a和b的最小公倍数表示为:
    lcm(a,b)。
    在这里有一个很重要的结论:当然,在求lcm的时候我们一般写成:
    这样可以防止ab溢出。
    7**. gcd的一些性质**
    gcd(a , b) = gcd(b , a-b)
    gcd(ma , mb) = m
    gcd(a , b), m为一个自然数
    gcd(a+mb , b) = gcd(a , b)
    m=gcd(a , b) 则gcd(a/m,b/m)=gcd(a,b)/m
    gcd(a, lcm(b, c)) = lcm(gcd(a, b), gcd(a, c))
    lcm(a, gcd(b, c)) = gcd(lcm(a, b), lcm(a, c))

请计算2个数的最大公约数和最小公倍数;(最大公约数可以使用辗转相除法,最小公倍数=2个数的乘积/它们的最大公约数;)
Input
输入数据有多组,每组2个正整数a,b(2<a,b<1000)
Output
在一行内输出a和b的最大公约数和最小公倍数;
Sample Input
15 10
Sample Output
5 3

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
/*int gcd(long long int a,long long int b)
{   long long int r;
     r=a%b;
     while(r)
      {   a=b;
          b=r;
          r=a&b;
      }
    return b;

}
int lcm(long long int a,long long int b)
{
    return a/lcm(a,b)*b;

}*/
long long gcd(long long a,long long b)
{return b?gcd(b,a%b):a;}
long long lcm(long long a,long long b)
{return a/gcd(a,b)*b;}
int main()
{}long long int n,m,k,l;
    while(scanf("%lld %lld",&n,&m)!=-1)
    {

        printf("%lld %lld",gcd(n,m),lcm(n,m));
    }

    return 0;
}

【小张自己来】

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
long long gcd(long long  a,long long  b)
{   long long r;
     r=a%b;
     while(r)
      {   a=b;
          b=r;
          r=a&b;
      }
    return b;
}
long long lcm(long long a,long long b)
{
  long long  l;
  l=a/gcd(a,b)*b;
  return l;
}
int main()
{
    long long int   n,m;
    while(scanf("%lld %lld",&n,&m)!=-1)
    {
    printf("%lld %lld",gcd(n,m),lcm(n,m));
    }

    return 0;
}

其实强大的C++内置了gcd函数!使用方法:__gcd(a,b)

#include <bits/stdc++.h>
using namespace std;
int main()
{
    long long a,b;
    while(cin>>a>>b)
        printf("%lld %lld\n",__gcd(a,b),a/__gcd(a,b)*b);
    return 0;
}

————————————————
DB最近沉迷于数论,她最近在研究最小公倍数和最大公约数,她的老师Z给她留了一个作业:在[x,y]区间中,求两个整数最大公约数是x且最小公倍数是y的个数。
Input
第一行输入一个T(T<=300),表示有T组数据,接下来输入两个数 x, y(1<=x<=y<=1e6)(含义如题)
Output
输出一行表示答案
Sample Input
1
2 12
Sample Output
4
Hint
(2,12) (4,6) (6,4) (12,2)

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int gcd(int a,int b)
{   int r;
     r=a%b;
     while(r)
      {   a=b;
          b=r;
          r=a%b;
      }
    return b;

}
int  lcm(int a,int b)
{  int p;
  p=a/gcd(a,b)*b;
    return p;
}
int main()
{
      int  x,y,i;
  int sum,n,j;
  scanf("%d\n",&n);
 while(n--)
     {
         scanf("%d %d",&x,&y);
         sum=0;
         for(i=x;i<=y;i+=x)
         {   if(y%i==0)
            {for(j=x;j<=y;j++)
             {  if(y%j==0)
              {if(gcd(i,j)==x&&lcm(i,j)==y)
                        sum++;
 }
             }}
 }
         printf("%d\n",sum);
 }
return 0;
}

有三个正整数a,b,c(0<a,b,c<10^6),其中c不等于b。若a和c的最大公约数为b,现已知a和b,求满足条件的最小的c。
Input
每行输入两个正整数a,b。
Output
输出对应的c,每组测试数据占一行
Sample Input
6 2
12 4
Sample Output
4
8

#include <bits/stdc++.h>
using namespace std;
int gcd(int a,int b)
{return b?gcd(b,a%b):a;}
int main()
{
    int a,b,i;
    while(cin>>a>>b)
    {
        for(i=b+1;;i++)
        {
            if(gcd(a,i)==b)
            {printf("%d\n",i);break;}
        }
    }
    return 0;
}

给定n(n<=10)个正整数,你的任务就是求它们的最大公约数,所有数据的范围均在long long内。
Input
输入数据有多组,每组2行,第一行为n,表示要输入数字的个数,接下来第二行有n个正整数。
Output
输出一个数,即这n个数的最大公约数。
Sample Input
5
2 4 6 8 10
2
13 26
Sample Output
2
13

#include <bits/stdc++.h>
using namespace std;
long long gcd(long long a,long long b)
{return b?gcd(b,a%b):a;}
int main()
{ long long n,i,a[11];
 while(cin>>n)
    {
        for(i=1;i<=n;i++)
            cin>>a[i];
        for(i=1;i<=n-1;i++)
            a[i+1]=gcd(a[i],a[i+1]);
        printf("%lld\n",a[n]);
    }
    return 0;
}
#include <bits/stdc++.h>
using namespace std;
long long gcd(long long a,long long b)
{return b?gcd(b,a%b):a;}
int main()
{
    long long n,i,a[11];
    while(cin>>n)
    {
        for(i=1;i<=n;i++)
            cin>>a[i];
        for(i=1;i<=n-1;i++)
            a[i+1]=gcd(a[i],a[i+1]);
        printf("%lld\n",a[n]);
    }
    return 0;
}

给定n(n<=10)个正整数,你的任务就是求它们的最小公倍数,所有数据的范围均在long long内。
Input
输入数据有多组,每组2行,第一行为n,表示要输入数字的个数,接下来第二行有n个正整数。
Output
输出一个数,即这n个数的最小公倍数。
Sample Input
5
2 4 6 8 10
2
13 26
Sample Output
120
26

#include <bits/stdc++.h>
using namespace std;
long long gcd(long long a,long long b)
{return b?gcd(b,a%b):a;}
long long lcm(long long a,long long b)
{return a/gcd(a,b)*b;}
int main()
{
    long long n,i,a[11];
    while(cin>>n)
    {
        for(i=1;i<=n;i++)
            cin>>a[i];
        for(i=1;i<=n-1;i++)
            a[i+1]=lcm(a[i],a[i+1]);
        printf("%lld\n",a[n]);
    }
    return 0;

x+y=a,lcm(x,y)=b;已知a和b求解x^2+
y^2
Input
多组数据输入。
第一行一个t表示a,b数对的数量。
接下来t行2个数表示a,b
T<=100000
a,b<10^9;
Output
每组样例输出t行每行一个数表示x2+y2;
Sample Input
2
6 4
6 3
Sample Output
20
18

#include <bits/stdc++.h>
using namespace std;
int gcd(int a,int b)
{return b?gcd(b,a%b):a;}
int main()
{
    int t,a,b;
    ios::sync_with_stdio(false);
    while(cin>>t)
    {
        while(t--)
        {
            cin>>a>>b;
            printf("%d\n",a*a-2*b*gcd(a,b));
        }
    }
    return 0;
}

今天西片同学又被高木同学捉弄了,高木同学跟西片同学玩了这么一个游戏。两人心中分别想一个数字,这两个数字分别为x和y(1<=x,y<=1e18),然后让西片同学说出一共有多少个整数既是x的因子,又是y的因子。由于西片和高木很有默契,所以保证他们两个想的数x和y的最大公因数不会超过1e9。这个问题又难住了西片同学了,你能帮帮西片同学告诉他答案吗?
Input
单组输入
数据占一行,包含两个整数x和y(1<=x,y<=1e18),保证gcd(x,y)<=1e9。
Output
输出既是x因子又是y因子的整数的个数。输出占一行
Sample Input
12 36
Sample Output
6

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x,y,ans=1;
int main()
{
    ios::sync_with_stdio(false);
    cin>>x>>y;
    ll k=__gcd(x,y);
    for(ll i=2;i*i<=k;i++)
    {
        ll c=0;
        while(k%i==0)
            k=k/i,c++;
        ans*=(c+1);
    }
    if(k>1)ans*=2;
    printf("%lld\n",ans);
    return 0;
}

给定A,B,C,计算AB%C,这里AB代表A的B次方。
Input
输入数据有多组,每组数据一行,有3个正整数分别为A,B和C,1<=A,B,C<=1000000000
Output
输出A^B%C的值
Sample Input
2 3 5
8 2 10
Sample Output
3
4

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
long long  quickmod(long long a,long long  b,long long c)
{
    int ret=1;
    while(b)
    {
        if(b&1)
            ret=ret*a%c;
        a=a*a%c;
        b/=2;
    }
return ret;
}

int main()
{ long long int x,y,z,q;
while(scanf("%lld %lld %lld",&x,&y,&z)!=-1)
      {  q=quickmod(x,y,z);
        printf("%d\n",q);
      }
return 0;
}

库特很喜欢做各种高深莫测的数学题,一天,她在书上看到了这么一道题。a[1]=6,a[2]=18;a[n]=2a[n-1]+3an-2,对于给出的某个数字n,求a[n]。库特一想这道题太简单了,可是看到n的范围是(n<=1e18),对于这么大范围的数,库特不知道该怎么做了,聪明的你,快来帮帮库特解决这个问题吧。(由于答案可能很大,请将答案对1e9+7(即1000000007)取模)。
Input
一个整数n(1<=n<=1e18)
Output
a[n]对1e9+7取模后的答案
Sample Input
5
Sample Output
486
乍看之下这是一道递推题,但是只要简单推理一下或者你多写几项你就能发现,a[n]=2*pow(3,n),求a[n]对1e9+7取模后的答案,所以这显然是一道快速幂取模的题目。套一下快速幂板子很容易就能得到答案了。

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
long long mode=1e9+7;
long long   mod(long long a,long long  b)
{
    int ret=1;
    while(b)
    {
      if(b&1)
            ret=ret*a%mode;
        a=a*a%mode;
        b/=2;
    }
 return ret;
}

long long int i,n;
int main()
{
  while(scanf("%lld",&n)!=-1)
      {      i=mod(3,n);
             i=i*2;
             i=i%mode;
             printf("%lld",i);
      }
return 0;
}

ljw这几天给大一同学讲课的时候,觉得自己太菜了,于是他做了一道无聊的数学题,
但是他觉得这题太难了,所以他把问题交给了聪明的你,你能帮他解决这个问题吗?

问题如下:
给你一个方程:n−(n^x)−x=0 (其中 ^ 表示两个数异或,并不是表示幂次符号哦)
现在给你n的值,问有多少种x的取值使得方程成立(数据保证n在2的30次方范围内)
Input
多组输入数据。
每组输入数据包含一个整数n,含义如题。
Output
每组输入数据输出一个整数,表示解的个数。
Sample Input
0
2
1073741823
Sample Output
1
2
1073741824
Hint
异或指的是,两个数的每一个二进制位,如果相同,异或结果为0,如果不相同,异或结果为1。
样例解释:
当n=0时,可以证明x只有等于0时才满足方程,
此时x只有1种满足条件的取值,所以输入的n=0时,你需要输出1。
先移项:a=x+(a^x),然后看二进制数的某一位,讨论即可发现规律:
a x+(a^x)
1 0+(1^0)==1
1 1+(1^1)==1
0 0+(0^0)0
0 1+(0^1)10
可以发现,第四种情况(a
0,x
1)会改变产生进位,改变了a的值,所以排除第四种情况。
综合第三,四种情况,可以发现,如果a的某一位为0的话,那么x对应位只能为0。
再来看第一,第二种情况,我们发现,当a的某一位为1时,x的对应位可以为1,可以为0。
所以,我们只需求出a的二进制数中,有多少个1即可,答案即为2^n

#include <bits/stdc++.h>
using namespace std;
int pre[35],a,k,sum;
int main(){
	pre[0]=1;
	for(int i=1;i<=30;i++)
	pre[i]=2*pre[i-1];//先打表
	while(cin>>a){
		for(int i=1;i<=30;i++)
		if(pre[i]>a){
			k=i-1;break;//求出a用二进制表示时有k位
		}
		sum=0;
		for(int i=0;i<=k;i++){
			if((1<<i)&a)
			sum++;
		}
		printf("%d\n",pre[sum]);
	}
	return 0;
}

移项a=(a⊕x)+x
可以发现x二进制位上为1的地方必定是a二进制位为1的地方。计算出a的二进制中1的个数为b。答案就是2^b。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const ll mod=1e9+7;
ll ksm(ll a,ll b)
{ ll res=1ll;
while (b)
 {
if (b&1)
 res*=a;
b>>=1
a=a*a;
}
return res;
}
int chan(int a)
{
 int res=0;
 while (a)
{
 if (a&1)
  res++;
 a>>=1;
}
 return res;
}
int main()
{
//freopen("std.in","r",stdin); //freopen("std.out","w",stdout);
 int a;
 while (~scanf("%d",&a))
 { ll ans=0;
 ans+=ksm(2,chan(a));        
printf("%lld\n",ans);    
}    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值