数论一日游

怎么说呢,数论其实常用的就是那么几个知识点,只要能熟练的掌握,其实还是挺简单的(呵呵)。下面是几道典型的题:

【noip2009 普及】细胞问题

Hanks 博士是 BT (Bio-Tech,生物技术) 领域的知名专家。现在,他正在为一个细胞实 验做准备工作:培养细胞样本。

Hanks 博士手里现在有 N 种细胞,编号从 1~N,一个第 i 种细胞经过 1 秒钟可以分裂为Si 个同种细胞(Si 为正整数)。 现在他需要选取某种细胞的一个放进培养皿,让其自由分裂,进行培养。一段时间以后,再把培养皿中的所有细胞平均分入 M 个试管,形成 M 份样本,用于实验。 Hanks 博士的试管数 M 很大,普通的计算机的基本数据类型无法存储这样大的M 值, 但万幸的是,M 总可以表示为 m1 的 m2 次方,即 M = m1^m2 ,其中 m1,m2均为基本数据类型可以存储的正整数。

注意,整个实验过程中不允许分割单个细胞,比如某个时刻若培养皿中有4个细胞, Hanks 博士可以把它们分入 2 个试管,每试管内 2 个,然后开始实验。 但如果培养皿中有 5 个细胞,博士就无法将它们均分入 2 个试管。 此时,博士就只能等待一段时间,让细胞们继 续分裂,使得其个数可以均分,或是干脆改换另一种细胞培养。

为了能让实验尽早开始,Hanks 博士在选定一种细胞开始培养后,总是在得到的细胞“刚 好可以平均分入 M个试管”时停止细胞培养并开始实验。现在博士希望知道,选择哪种细 胞培养,可以使得实验的开始时间最早。

输入格式

第一行有一个正整数 N,代表细胞种数。

第二行有两个正整数 m1,m2,以一个空格隔开, m1^m2 即表示试管的总数 M。

第三行有 N 个正整数,第 i 个数 Si 表示第 i 种细胞经过 1 秒钟可以分裂成同种细胞的个 数。

输出格式

共一行,为一个整数,表示从开始培养细胞到实验能够开始所经过的 最少时间(单位为秒)。 如果无论 Hanks 博士选择哪种细胞都不能满足要求,则输出整数-1。

样例数据

input

输入样例1:
1
2 1
3

输入样例2:
2
24 1
30 12


output

输出样例1:
-1

输入样例2:
2

数据规模

对于 50%的数据,有 m1^m2≤ 30000。 对于所有的数据,有 1 ≤ N ≤ 10000,1 ≤ m1 ≤ 30000,1 ≤ m2 ≤ 10000,1 ≤ Si ≤ 2,000,000,000。

时间限制:1s
空间限制:256MB

解析

题目中要求细胞分裂之后被试管整除,又因为试管有m1^m2只,那么很明显,只要能被m1整除,就能被m1^m2整除,所以要先判断能不能被分列之后能不能被m1整除。

那么我们可以将m1和s[i]质因数分解,只要m1的质因数s[i]中都有,那么s[i]经过多次方之后一定能被m1整除(显然可得)。

接下来就是求分裂次数。因为m1=p1^a1*p2^a2…pn^an 所以m1^m2=p1^(a1*m2)*p2^(a2*m2)…。同理,s[i]^n=p1^(b1*n)*p2^(b2*n)…。
(s[i]^n)/(m1^m2)的条件就是b1*n,b2*n,…,bn*n都大于等于a1^m2,a2^m2,…,an^m2;
所以最小分裂次数就是他们每一个值之间相差的最大倍数。总的答案就是这些最大倍数之间取最小值。

代码

#include<bits/stdc++.h>
using namespace std;
int n,m1,m2,a[10050],t,num1[1000],b[1000],ans,ctt=1e9;
bool f[30050];
inline int read(){
    int NUM=0,f=1;
    char c=getchar();
    for(;c<'0'||c>'9';c=getchar())
    if(c=='-')f=-1;
    for(;c>='0'&&c<='9';c=getchar())
    NUM=(NUM<<1)+(NUM<<3)+c-48;
    return NUM*f;
}
void init(){
    n=read();
    m1=read();m2=read();
    if(m1==1){
        printf("0");
        exit(0);
    }
    for(int i=1;i<=n;++i)a[i]=read();
}
void work(){
    f[1]=1;
    for(int i=2;i<=m1;++i)
        if(!f[i])
            for(int j=2;j<=m1/i;++j)
                f[i*j]=1;
    for(int i=2;i<=m1;++i)//分解m1
        if(!f[i]&&m1%i==0){
            b[++t]=i;
            int x=m1;
            while(x%i==0){
                num1[t]++;
                x/=i;
            }
            num1[t]*=m2;
        }
    for(int i=1;i<=n;++i){//用m1的质因数去除s[i]看是否能整除,并顺便求出有几个。
        bool flag=1;
        ans=-1000;
        for(int j=1;j<=t;++j){
            if(a[i]%b[j]!=0){//s[i]中没有这个质因数
                flag=0;
                break;
            }
            int x=a[i],num=0,timee;
            while(x%b[j]==0){
                num++;
                x/=b[j];
            }
            if(num1[j]%num==0)timee=num1[j]/num;//算质因数指数之间相差的倍数,向上取整。
            else timee=num1[j]/num+1;
            ans=max(ans,timee);
        }
        if(!flag)continue;
        ctt=min(ctt,ans);
    }
    if(ctt==1e9)printf("-1");
    else
    printf("%d",ctt);
}
int main(){
    init();
    work();
    return 0;
}

分数分解

给定n(2≤n≤10^9)值,要求x、y均为正整数,且x

输入格式

一个整数n。

输出格式

一个整数,表示相应的方法数是多少。

样例数据

input

6


output

4

数据规模和约定

时间限制:1s
空间限制:256MB

解析

这道题挺简单的,但是一个数轮中常见的公式变幻的类型:
因为 1/x+1/y=1/n;
所以 (x+y)/xy=1/n;
所以 n(x+y)=xy;
移相得 xy-n(x+y)=0;
两边加上n^2得 n^2-n(x+y)+n^2=n^2;
根据十字相乘法 (n-x)(x-y)=n^2;
于是这道题就转换成了求n^2的约数;
因为n=p1^a1*p2^a2*…*pn^an;
所以n^2=p1^(2a1)*p2^(2a2)..pn^(2an);
所以n^2的约数个数为(2a1+1)*(2a2+1)…(2an+1);
最后因为x

代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5;
int n,num[MAXN],t,ans=1;
inline int read(){
    int NUM=0,f=1;
    char c=getchar();
    for(;c<'0'||c>'9';c=getchar())
    if(c=='-')f=-1;
    for(;c>='0'&&c<='9';c=getchar())
    NUM=(NUM<<1)+(NUM<<3)+c-48;
    return NUM*f;
}
void init(){
    n=read();
}
void work(){
    for(int i=2;i*i<=n;++i){
        if(n%i==0)t++;
        while(n%i==0){
            num[t]++;
            n/=i;
        }
    }
    if(n>1)num[++t]=1;
    for(int i=1;i<=t;++i)ans*=(2*num[i]+1);
    ans/=2;
    printf("%d",ans);
}
int main(){
    init();
    work();
    return 0;
}

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 整除。

输出格式

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

样例数据

input

2
41 1 96 288
95 1 37 1776


output

6
2

数据规模与约定

对于 50%的数据,保证有 1≤a0,a1,b0,b1≤10000且n≤1001≤a0,a1,b0,b1≤10000且n≤100。 对于 100%的数据,保证有 1≤a0,a1,b0,b1≤2,000,000,000且n≤20001≤a0,a1,b0,b1≤2,000,000,000且n≤2000。

解析

这道题其实就是考了gcd的性质:
1、gcd(x,a0)=a1;
gcd(x/a1,a0/a1)=1;
2、x*b0/gcd(x,b0)=b1;
gcd(x,b0)=x*b0/b1;
gcd(b1/b0,b1/x)=1;

代码

#include<bits/stdc++.h>
using namespace std;
int n,a0,a1,b0,b1,x,ans;
inline int read(){
    int NUM=0,f=1;
    char c=getchar();
    for(;c<'0'||c>'9';c=getchar())
    if(c=='-')f=-1;
    for(;c>='0'&&c<='9';c=getchar())
    NUM=(NUM<<1)+(NUM<<3)+c-48;
    return NUM*f;
}
int gcd(int a,int b){
    if(b==0)return a;
    return gcd(b,a%b);
}
void work(){
    ans=0;
    for(int i=1;i*i<=b1;++i)
        if(b1%i==0){
            if(i%a1==0&&gcd(i/a1,a0/a1)==1&&gcd(b1/b0,b1/i)==1)ans++;
            int j=b1/i;
            if(i!=j&&j%a1==0&&gcd(j/a1,a0/a1)==1&&gcd(b1/b0,b1/j)==1)ans++;
        }
    printf("%d\n",ans);
}
void init(){
    n=read();
    for(int i=1;i<=n;++i){
        a0=read();a1=read();b0=read();b1=read();
        work();
    }
}
int main(){
    init();
    return 0;
}

———-

再也不想做数论了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值