[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] 质数密度
题目描述
输入
第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] 数论题目复习的主要内容,如果未能解决你的问题,请参考以下文章