[SinGuLaRiTy] 数论题目复习

Posted SinGuLaRiTy2001

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SinGuLaRiTy] 数论题目复习相关的知识,希望对你有一定的参考价值。

【SinGuLaRiTy-1020】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.

[CQBZOJ 1464] Hankson

题目描述

Hanks博士是BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫Hankson。现在,刚刚放学回家的Hankson正在思考一个有趣的问题。 今天在课堂上,老师讲解了如何求两个正整数c1和c2的最大公约数和最小公倍数。现在Hankson认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:

已知正整数a0,a1,b0,b1,设某未知正整数x满足:

1. x和a0的最大公约数是a1;

2. x和b0的最小公倍数是b1。

Hankson的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的 x并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x的个数。请你帮助他编程求解这个问题。

输入

第一行为一个正整数n,表示有n组输入数据。接下来的n行每行一组输入数据,为四个正整数a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入数据保证a0能被a1整除,b1能被b0整除。

输出

输出共n行。每组输入数据的输出结果占一行,为一个整数。 对于每组数据:若不存在这样的x,请输出0; 若存在这样的x,请输出满足条件的x的个数。

样例数据

样例输入 样例输出
2
41 1 96 288
95 1 37 1776
6
2 

 

 

 

 

解析

    ∵lcm(x,b0)*gcd(x,b0)=x*b0
    ∴lcm(x,b0)=x*b0/gcd(x,b0)
又∵lcm(x,b0)=b1
    ∴x*b0/gcd(x,b0)=b1
    即b1*gcd(x,b0)=x*b0
       gcd(x,b0)=x*b0/b1
       gcd[x/(x*b0/b1),b0/(x*b0/b1)]=x*b0/b1/(x*b0/b1)
    得gcd(b1/b0,b1/x)=1
    ∴x为b1的约数,即x|b1
又∵由题意得:a1为x的约数,即x%a1=0
    ∵题目中有:① gcd(x,a0)=a1
                         ② lcm(x,b0)=b1
    ∴由②得:gcd(b1/b0,b1/x)=1 ③
        由①得:gcd(x/a1,a0/a1)=1 ④
    由③④可对枚举进行优化.

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std; 

int cnt,tot;
int a0,a1,b0,b1;

int gcd(int a,int b)
{
    return b==0 ? a:gcd(b,a%b);
}

bool calculation(long long x)
{
    if(x%a1!=0)
        return 0;
    return gcd(x/a1,a0/a1)==1&&gcd(b1/b0,b1/x)==1;
} 

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
        int ans=0;
        for(int i=1;i*i<=b1;i++)
        {
            if(b1%i==0)
            {
                ans+=calculation(i);
                if(b1/i!=i)
                    ans+=calculation(b1/i);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

[CQBZOJ 2642] 无平方因子的数

题目描述

给出正整数n和m,区间[n, m]内的“无平方因子”的数有多少个?整数p无平方因子当且仅当不存在 k > 1,使得p是k^2 的倍数。

输入

第1行:2个整数n和m (1 <= n <= m <= 10^9, m - n <= 10^7)

输出

第1行:1个整数,表示区间中无平方因子的数的个数Cnt

样例数据

样例输入 样例输出
1 10 7

 

 

解析

可以在区间[n,m]中for循环筛选出有平方因子的数,做到这一点很简单:对于每一个平方因子数k*p*p,只需枚举k,p,循环(可进行一些小优化)途中进行计数,最后用区间内的总个数减去有平方因子的数就可以得到答案了。

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
bool v[10000002];
int p[10000010];
int k;
void Prime(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!v[i]) 
            p[++k]=i;
        for(int j=1;j<=k&&p[j]*i<=n;j++)
        {
            v[i*p[j]]=1;
            if(i%p[j]==0) 
                break;
        }
    }
    memset(v,0,sizeof(v));
}
int main()
{
    int n,m,s=0;
    scanf("%d%d",&n,&m);
    Prime(30000);
    for(int i=1;p[i]*p[i]<=m;i++)
        for(int j=p[i]*p[i];j<=m;j+=p[i]*p[i])
            if(j>=n&&!v[j-n]) 
                v[j-n]=1,s++;
    printf("%d",m-n+1-s);
    return 0;
}

 

[POJ 2407] 欧拉函数的值

题目描述

给定整数n,求n的欧拉函数的值。

输入

多组数据

每行一个整数,表示n( 1 <= n <= 1,000,000,000);一个0,表示输入结束

输出

每行输入一个整数,表示对应的n的欧拉函数值

样例数据

样例输入 样例输出
7
12
0
6
4

 

 

 

 

解析

似乎对欧拉函数已经有些陌生了:不超过n且与n互质的正整数。

对于求解欧拉函数,似乎并没有什么便于理解的逻辑推理,只有背板了。

Code

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    int n,i,temp;
    while(scanf("%d",&n)!=EOF)
    {
        temp=n;
        for(i=2;i*i<=n;i++)
        {
          if(n%i==0)
          {
              while(n%i==0) n=n/i;
              temp=temp/i*(i-1);
          }
          if(n<i+1)
              break;
        }
        if(n>1)
            temp=temp/n*(n-1);
        printf("%d\n",temp);
    }
    return 0;
}

 

[CQBZOJ 2948] 质数密度

题目描述

我们定义区间[L, R]的质数密度为区间中质数的个数除以区间中数的个数。例如,区间[1,10]中的质数有2,3,5,7这4个,所以质数密度为4 / 10 = 0.4
给出区间的左、右端点,请计算出区间的质数密度。

输入

第1行:2个整数L, R,表示区间的左、右端点(1<=L < R <= 10^5)

输出

第1行:1个浮点数,表示答案。结果保留7位小数。

样例数据

样例输入 样例输出
1 10
0.400000

 

 

 

解析

需运用线性筛素数。基本思路为:先假设所有的数都是素数,运用“所有素数乘一个不为1的整数都为合数”这一定理,将得到的合数一一筛去。当然,在该算法的基础上有多种优化。

这里有一篇不错的写线性筛素数法的文章:[一般筛法求素数+快速线性筛法求素数]

Code

#include<cstdio>
#include<cmath>
using namespace std;
int main()
{
    int L,R,m,cnt=0;
    double ans;
    bool flag;
    scanf("%d%d",&L,&R);
    for(int i=(L==1)?2:L;i<=R;i++) {
        m=(int)(sqrt(i)+0.5);
        flag=true;
        for(int j=2;j<=m;j++)
            if(i%j==0)
            {
                flag=false;
                break;
            }
        if(flag)
            cnt++;
    }
    ans=1.0*cnt/(R-L+1);
    printf("%.7f\n",ans);
    return 0;
}

 

[USACO 3.2.1] 阶乘

题目描述

N的阶乘写作N!表示小于等于N的所有正整数的乘积。 阶乘会很快的变大,如13!就必须用32位整数类型来存储,70!即使用浮点数也存不下了。

你的任务是找到阶乘最后面的非零位。

举个例子: 5!=1*2*3*4*5=120所以5!的最后面的非零位是2 7!=1*2*3*4*5*6*7=5040,所以最后面的非零位是4.

输入

共一行,一个整数不大于4,220的整数N。

输出

共一行,输出N!最后面的非零位。

样例数据

样例输入 样例输出
7
4

 

 

 

解析

要解这道题,首先就要知道末尾的零是如何产生的:

1>末尾分别为5和2的数相乘会得到“0”;

2>乘10的倍数时会得到“0”;

现在,我们一旦将这些数(注意:只有2与5成对出现时才可以抵消,单出来的不能剔除)剔除出去,再不断相乘,对乘积保留末尾的数,再将其乘起来......乘完了所有的数,答案也就得出来了。

(当然,也可以对乘积不断模10,这实际上也实现了上述操作)。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
int main()
{
    int n,ans=1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        ans=(ans*i)%1000000;
        while(ans%10==0) 
            ans=ans/10;
    }
    printf("%d",ans%10);
    return 0;
}

 

[POJ 1845] 幂的约数之和

题目描述

给定正整数A, B,求A^B的所有因数之和,并模9901。

输入

仅一行,有两个整数A和B(0 <= A,B <= 50000000)。

输出

输出仅一行:问题的答案。

样例数据

样例输入 样例输出
2 3
15

 

 

 

解析

 

以上是关于[SinGuLaRiTy] 数论题目复习的主要内容,如果未能解决你的问题,请参考以下文章

[SinGuLaRiTy] 分治题目复习

[SinGuLaRiTy] 图论题目复习

[SinGuLaRiTy] 动态规划题目复习

[SinGuLaRiTy] 组合数学题目复习

[SinGuLaRiTy] 复习模板-搜索

[SinGuLaRiTy] 复习模板-数学