返回顶部

牛客竞赛(gcd,快速幂)

一、最大公约数和最小公倍数问题

题目描述:

输入2个正整数x0,y0(2<=x0<100000,2<=y0<=1000000),求出满足下列条件的P,Q的个数。

条件:1.P,Q是正整数;
2.要求P,Q以x0为最大公约数,以y0为最小公倍数。

试求: 满足条件的所有可能的两个正整数的个数。

输入描述:

每个测试文件包含不超过5组测试数据,每组两个正整数x0和y0(2<=x0<100000,2<=y0<=1000000)。

输出描述:

对于每组输入数据,输出满足条件的所有可能的两个正整数的个数。

下面是对样例数据的说明:

输入3 60

此时的P Q分别为:

3 60
15 12
12 15
60 3

所以,满足条件的所有可能的两个正整数的个数共4种。

#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b){
    if(b==0)
        return a;
    return gcd(b,a%b);
}
 
int main(){
    int x,y;
    while(~scanf("%d%d",&x,&y)){
        int ans=0;
        int mul=x*y;
        for(int i=x;i<=y;i++){
        if(mul%i!=0)
            continue;
        int k=mul/i;
        if(x==gcd(i,k))
            ans++;
    }
    cout<<ans<<endl;
    }
    return 0;
}

gcd(欧几里得又称辗转相除)算法

定理:两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。最大公约数(Greatest Common Divisor)缩写为GCD。
gcd(a,b) = gcd(b,a mod b) (不妨设a>b 且r=a mod b ,r不为0)
证明 :a可以表示成a = kb + r(a,b,k,r皆为正整数,且r<b),则r = a mod b
假设d是a,b的一个公约数,记作d|a,d|b,即a和b都可以被d整除。
而r = a - kb,两边同时除以d,r/d=a/d-kb/d=m,由等式右边可知m为整数,因此d|r
因此d也是b,a mod b的公约数
假设d是b,a mod b的公约数, 则d|b,d|(a-k*b),k是一个整数。
进而d|a.因此d也是a,b的公约数
因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证。

以下是gcd算法C++实现代码:

int Gcd(int a, int b)
{
    if(b == 0)
        return a;
    return Gcd(b, a % b);
}

二、筱玛爱地理

点击查看题目描述

  • 这道看似简单的题把好多人都卡死在了分数取模上
  • 赛后题解中涉及到我这个蒟蒻的很多知识盲区orz
  • 先上代码再总结:
#include<bits/stdc++.h>
#define int long long
/*不管32位还是64位的,int都是4个字节,一个字节8位,即32位。 
long long 都是8个字节,就是64位,这句话的作用就是将原来的int_32 型 改成int_64型,
在一些预编译里面还会出现 #define _int64 long long的语句,c99标准之后,64位的int都用long long 来表示,这里可以理解为处理标准兼容的问题。 */
#define N 500005
using namespace std; 
const int Mod=1e9+7;//科学计数法 
struct node{
    int x, y;
}a[N];//定义结构体的同时,定义大小为N的结构体数组。 
inline bool cmp(node aa,node bb)
{
    return aa.y*bb.x>aa.x*bb.y;	//避免精度问题所以采用交叉相乘的方式比较大小
} 
inline int ksm(int x,int y)//x是底数,y是指数
{//快速求模运算。 
	int ans1=1;
	while (y)
    {
        if (y&1) //逐位“与”运算,通过和1相与,保留最后一位 
        {
            ans1=1ll*ans1*x;
            ans1=ans1%Mod;//ans1记录前面每次翻倍求模结果的乘积再求模
        }
		  /*通过乘以1ll,将等号右边的精度提高到long long ,低精度向高精度转化,避免中间结果溢出范围,右边的ans现在是64位的int,原来32位的int型 10位数字,long long 19位数字, 1e+9是9位数字, 所以是32位还是64位都不会超出范围。 */
         y>>=1;//右移1位相当除以2,类比十进数,右移一位相当于除以10,同理左移,是乘以相应的进制基数。 
		x=1ll*x*x%Mod;//x中记录每次翻倍的求模的结果。 
     }
	 return ans1;
}
signed main()
{
    int n;
 	scanf("%lld",&n);
    for (int i=1;i<=n;i++) 
	scanf("%lld%lld",&a[i].x,&a[i].y);//注意结构体的输入方式。 
    sort(a+1,a+n+1,cmp);//i=1开始,所以起始位置为a+1 
    for (int i=1;i<=n;i++) 
	printf("%lld\n",a[i].y*ksm(a[i].x,Mod-2)%Mod);//分数求模的定理转换。 
    return 0;
}

(一)inline关键字

1.在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。
栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。
在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。
2.inline 的使用是有所限制的,inline 只适合涵数体内代码简单的函数使用,不能包含复杂的结构控制语句例如 while、switch,并且不能内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)。
3.inline 函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思,它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。
慎用inline!!!
内联是以代码膨胀(复制) 为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。
如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。

(二)求模运算

  • 费马小定理:对于质数p,任意整数a,均满足:$$a^p\equiv a(mod p)$$
  • 以下推导出分数求模
    • \[a^{p-1}modp\equiv1modp \]

    • \[a^{p-2}*amodp\equiv1modp \]

    • \[a^{p-2}modp\equiv\dfrac{1}{a}modp \]

    • \[a^{-n}modp\equiv a^{p-n-1}modp \]

  • 题中要求\(\beta =V/E\)\(\beta\)的模,即可转化为:$$\beta mod p=V*\dfrac{1}{e}modp$$ (其中p=1e10+7)
  • 模运算具有以下性质:

    • \[(A+B)modp=(Amodp+Bmodp)modp \]

    • \[(A*B)modp=((Amodp)*(Bmodp))modp$$</p> \]

    (因为V<p所以mod后不变)

(三)按位与运算

  • 判断奇偶一般我用的是取余运算,今天学到还可以按位与判断奇偶
  • 按位与的优点:效率更高,时间更快
  • 原理:
    • 按位与是将两个数转化为二进制,若对应的位两数都为1,则结果中该位为1,否则该位为0
    • 一个数如果与1进行按位与运算,奇数转化为二进制后最后一位肯定为1,偶数肯定为0
if((x&1)==1)
	printf("奇数");
else if((x&1)==0)
	printf("偶数");
posted @ 2019-07-13 10:15  席懿  阅读(888)  评论(0编辑  收藏  举报