专题十四 数论基础
- LightOJ 1370 Bi-shoe and Phi-shoe
- LightOJ 1356 Prime Independence
- LightOJ 1341 Aladdin and the Flying Carpet
- LightOJ 1336 Sigma Function
- LightOJ 1282 Leading and Trailing
- LightOJ 1259 Goldbach`s Conjecture
- LightOJ 1245 Harmonic Number (II)
- LightOJ 1236 Pairs Forming LCM
- LightOJ 1234 Harmonic Number
- LightOJ 1220 Mysterious Bacteria
- LightOJ 1214 Large Division
- LightOJ 1213 Fantasy of a Summation
- LightOJ 1197 Help Hanzo
- LightOJ 1138 Trailing Zeroes (III)
- UVA 11426 GCD - Extreme (II)
- UVA 11754 Code Feat
- UVA 11916 Emoogle Grid
- POJ 1061 青蛙的约会
- POJ 2115 C Looooops
- POJ 2116 Death to Binary?
- HDU 2161 Primes
- UVA 11827 Maximum GCD
- UVA 10200 Prime Time
- SGU 106 The equation
- POJ 2478 Farey Sequence
- UVA 11752 The Super Powers
LightOJ 1370 Bi-shoe and Phi-shoe
Bamboo Pole-vault是Xzhiland的一项大受欢迎的运动。Phi-shoe大师是他成功的非常受欢迎的教练。他需要为他的学生提供一些竹子,所以他让他的助手Bi-Shoe去市场购买。市场上有很多可能的整数长度的Bamboos(是的!)。根据Xzhila的传统,
竹子的分数= Φ(竹子的长度)
(Xzhilans非常喜欢数论)。对于您的信息,Φ(n) =小于n的数字,它们相对于素数(除了1之外没有公约数)到n。因此,长度为9的竹子的得分为6,因为1,2,4,5,7,8是9的相对素数。
助理双鞋必须为每个学生买一个竹子。作为一个扭曲,Phi-shoe的每个撑杆跳学生都有一个幸运数字。Bi-shoe希望购买竹子,这样他们每个人都会得到一张分数大于或等于他/她的幸运数字的竹子。Bi-shoe希望最大限度地减少购买竹子所花费的总金额。一个竹子单位花费1 Xukha。帮助他
输入
输入以整数T(≤100)开始,表示测试用例的数量。
每个案例都以包含整数n(1≤n≤10000)的行开头,表示Phi-shoe的学生人数。下一行包含n个空格分隔的整数,表示学生的幸运数字。每个幸运数字都在[1, ]范围内。
产量
对于每种情况,打印案例编号和购买竹子所花费的最少金额。有关详细信息,请参阅示例
样本输入
3
5
1 2 3 4 5
6
10 11 12 13 14 15
2
1 1
样本输出
Case 1: 22 Xukha
Case 2: 88 Xukha
Case 3: 4 Xukha
上面第一的定理可以知道欧拉函数的值是比下标要小的,而且下标最小是从欧拉函数的值+1开始,即值是a,下标是a+1,当然这种情况是a+1为素数时产生,不是素数其实也不用担心,从这个点开始往后枚举,也可以保证找到的下标是最小的,题目要求的就是这些数对应的下标最小值的所有累加和
- 定理1:若:k为素数,那么:φ(k) = k-1
- 定理2:若:p是素数,且n = p^k,那么φ(n) = (p-1)*p^(k-1)
- 定理3:若:p, q互质,那么:φ(p*q) = φ(p) * φ(q)
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long int
#define MAX 2000000+5
int euler[MAX];
void Euler(){
for(int i=2;i<MAX;i++)
euler[i] = i;
for(int i=2;i<MAX;i++){
if(euler[i]==i)
for(int j=i;j<MAX;j+=i)
euler[j] = euler[j]/i*(i-1);
}
}
int main(){
Euler();
int T,n,a,cnt=1;
ll ans;
scanf("%d",&T);
while(T--){
ans = 0;
scanf("%d",&n);
while(n--){
scanf("%d",&a);
for(int i=a+1;;i++)
if(euler[i]>=a){
ans+=i;
break;
}
}
printf("Case %d: %lld Xukha\n",cnt,ans);
cnt++;
}
return 0;
}
LightOJ 1356 Prime Independence
如果一个整数的成员都不是另一个成员的素数倍,则它被称为素数独立。一个整数,一个被说成是一个素数多的b如果
a = bxk(其中k是素数[1])
所以,6是2的素数倍,但是8不是。例如,{2,8,17}是素数独立的,但{2,8,16}或{3,6}不是。
现在,给定一组不同的正整数,计算最大的素数独立子集。
输入
输入以整数T(≤20)开始,表示测试用例的数量。
每种情况都以整数N(1≤N≤40000)开始,表示集合的大小。下一行包含由单个空格分隔的N个整数。这些N个整数中的每一个都是不同的,并且在1到500000之间。
输出
对于每种情况,打印案例编号和最大素数独立子集的大小。
样本输入
3
5
2 4 8 16 32
5
2 3 4 6 9
3
1 2 3
样本输出
Case 1: 3
Case 2: 3
Case 3: 2
注意
1. 一个整数,被认为是首要,如果它是整除恰好两个不同的整数。前几个素数是2,3,5,7,11,13 ......
2. 数据集很大,使用更快的I / O方法。
涉及二分图
LightOJ 1341 Aladdin and the Flying Carpet
据说阿拉丁在获得魔法之光之前必须解开七个谜团才能召唤出一个强大的精灵。在这里,我们关注第一个谜。
阿拉丁即将进入一个神奇的洞穴,由邪恶的巫师领导,他伪装成阿拉丁的叔叔,在入口处发现了一个奇怪的神奇飞毯。有一些奇怪的生物守卫着洞穴的入口。阿拉丁可以跑,但他知道有很高的机会被抓住。所以,他决定使用神奇的飞毯。地毯呈矩形,但不是方形。阿拉丁拿走了地毯,在他的帮助下,他经过了入口。
现在,您将获得地毯的区域和地毯最小可能的长度,您的任务是找到可能的地毯类型。例如,地毯12的面积和地毯的最小可能侧面是2,那么可以有两种类型的地毯,它们的侧面是:{2,6}和{3,4}。
输入
输入以整数T(≤4000)开始,表示测试用例的数量。
每种情况下,开始用含两个整数的行:a b (1≤b≤a≤10 12),其中a表示地毯的面积和b表示地毯的最小可能侧。
输出
对于每种情况,请打印案例编号和可能的地毯数量。
样本输入
2
10 2
12 2
样本输出
Case 1: 1
Case 2: 2
题目大意:给出面积n,和最短边m,求能形成的矩形的个数(不能为正方形)。
题目思路:根据算数基本定理有:
1.每个数n都能被分解为:n=p1^a1*p2^a2*^p3^a3……pn^an(p为素数);
2.n的正因数的个数sum为:sum=(1+a1)*(1+a2)*(1+a3)……(1+an);
最短边为m,若m>=sqrt(n),则无解。所以m最多10^6,可遍历找出1-m中n的因子,并用sum去减去这类因子的个数。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define ll long long
#define MAX 1000005
int p[MAX];//存储所有的素数
int v[MAX];//倍筛表 可以用bool数组省点空间
ll t=0;//记录当前素数的个数(下标)
void getPrime(){
memset(p,0,sizeof(p));
memset(v,0,sizeof(v));
for(int i=2;i<MAX;i++)
if(!v[i]){
p[t++] = i;
for(int j=i;j<MAX;j+=i)
v[j] = 1;
}
}
ll Ans(ll n){
ll ans = 1,cnt;
for(int i=0;i<t&&p[i]<=n;i++){
if(n%p[i]==0){
cnt = 0;
while(n%p[i]==0){
n/=p[i];
cnt++;
}
ans*=(1+cnt);
}
}
if(n>1)//说明还有一个素数是它的约数,此时cnt=1
ans*=2;
return ans;
}
int main(){
int T,cnt=1;
ll a,b;
getPrime();
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&a,&b);
ll ans=0;
if(a/b<b){
printf("Case %d: %lld\n",cnt++,ans);
continue;
}
ans = Ans(a)/2;//对称性,题目只要一种,且2边不会相等
for(int i=1;i<b;i++)
if(a%i==0)
ans--;
printf("Case %d: %lld\n",cnt++,ans);
}
return 0;
}
LightOJ 1336 Sigma Function
西格玛函数是数论中一个有趣的函数。它用希腊字母Sigma(σ)表示。该函数实际上表示数字的所有除数之和。例如,σ(24)= 1 + 2 + 3 + 4 + 6 + 8 + 12 + 24 = 60。小数字的西格玛很容易找到,但对于大数字,很难直接找到。但是数学家们发现了一个找到西格玛的公式。如果整数的素数幂分解是
然后我们可以写,
对于某些n,σ(n)的值是奇数,而对于其他n,它是偶数。给定值ñ,你必须找到多少从整数1至ň有甚至价值σ。
输入
输入以整数T(≤100)开始,表示测试用例的数量。
每种情况下开始与包含整数的线N(1≤N≤10 12)。
输出
对于每种情况,打印案例编号和结果。
样本输入
4
3
10
100
1000
样本输出
Case 1: 1
Case 2: 5
Case 3: 83
Case 4: 947
首先给出题目中的公式的推导过程:
n是一个整数,f(n)代表他的因子的和。假设n=12,对他进行素因子分解可得n=2^2*3。12的因子有1,2,3,4,6,12,和为28。根据题目中的公式:f(n)=(2^3-1)/(2-1)*(3^2-1)/(3-1)=7*4=28。为什么会是这样呢?将因子再进行素因子分解可以发现:1=2^0*3^0 , 2=2^1*3^0 , 3=2^0*3^1 , 4=2^2*3^0 , 6=2^1 *3^1 , 12=2^2*3^1。所以1+2+3+4+6+12=2^0*3^0+2^1*3^0+2^0*3^1+2^2*3^0+2^1 *3^1+2^2*3^1=(2^0+2^1+2^2)*(3^0+3^1)。利用等比数列前n项和公式:(2^3-1)/(2-1)*(3^2-1)/(3-1)=7*4=28。推导完毕。
事实上,这称之为积性函数。
解题思路:
题意:
求 1—n 中,有多少个数的因子和是偶数。
题解:
打表找规律。
素因子分解打表计算前n项和判断奇数偶数可以发现如下规律:
只要是2^x,a^2,2*a^2...只有这种数的因子和是奇数。所以,我们直接去重即可。
但是这些直接去重我们会发现减去的这些值有重复的,所以我们要判断下。
i (代表x||a): 0 1 2 3 4 5 6 7 8 9 ......
2^x: 1 2 4 8 16 32 64 128 ......
a^2: 0 1 4 9 16 25 36 49 64 ......
2*a^2: 0 2 8 18 32 50 72 ......
我们可以发现2^x里面有的数,a^2和2*a^2里面都有。
加下划线的字一一对应,加粗的字一一对应。
①2^x和a^2, 当x为偶数时二者出现重复。
②2^x和2*a^2,当x为奇数时,二者出现重复。
所以不需要考虑2^x的个数,直接用n减去a^2和2*a^2的个数就是我们要的结果。
易知:a^2的个数=sqrt(n),2*a^2的个数=sqrt(n/2)。
那么为什么会是这样呢?给出推导过程:
n=p1^e1*p2^e2...,则f(n)=(p1^(e1+1)-1)/(p1-1))*(p2^(e2+1)-1)/(p2-1))....
且(p1^(e1+1)-1)/(p1-1))=p1^0+p1^1......+p1^e1;
要使得f(n)为奇数,则(p1^(e1+1)-1)/(p1-1)到(pn^(en+1)-1)/(pn-1)都要为奇数;
因为奇数*奇数=奇数,奇数*偶数=偶数;
1)当p=2时,2^(e+1)-1,一定为奇数;
2)当p!=2时,则p为奇数(因为p是素因子),则当e为偶数时(p^(e+1)-1)/(p-1)为奇数。
经转化我们可以发现,2^6=8^2,2^11=2*32^2。也就是平方数和2倍的平方数。
则需要统计1到n中的平方数个数和2倍的平方数的个数,得到的为1到n中f(n)为奇数的个数。
#include<cstdio>
#include<cmath>
using namespace std;
int main()
{
long long n;
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
scanf("%lld",&n);
long long a1=sqrt(n);
long long a2=sqrt(double(n/2));
printf("Case %d: %lld\n",i,n-a1-a2);
}
return 0;
}
LightOJ 1282 Leading and Trailing
你有两个整数:n和k,你的任务是找到最重要的三位数,最低三位数n k。
输入
输入以整数T(≤1000)开始,表示测试用例的数量。
每种情况下,开始用含两个整数的行:N(2≤Ñ<2 31)和K(1≤ķ≤10 7)。
输出
对于每种情况,打印案例编号和三个前导数字(最重要)和三个尾随数字(最不重要)。您可以假设输入是这样的,即n k至少包含六位数。
样本输入
5
123456 1
123456 2
2 31
2 32
29 8751919
样本输出
Case 1: 123 456
Case 2: 152 936
Case 3: 214 648
Case 4: 429 296
Case 5: 665 669
这题的要求前3位和后3位,后3位简单,直接mod1000,前3位就有点难度了,这里是用double模拟快速幂的过程,超过1000.0就除一次,注意需要≥1000.0,因为没写"="号,导致无法AC,注意输出为3位,如果后三位是023,那么必须输出023,不能输出23
方法2:
题意:求一个数n的k次方后的前三位与后三位。并且后三位要求控制格式。
思路:这道题后三位可以用快速幂求出来,前三位就要用到log了。先说一下怎么求n^k的前三位。
我先设10^p=n^k,同时取log10,那么p=k*log10(n)。再设x=(int)p(整数部分),y=p-x(小数部分),那么10^p=10^x*10^y;由于10^x是10的倍数,那么10^y=n^k/10^x,就是n^k的值,不过就是小数点位置不同。那么只要求得y就能知道前三位的值了。
计算方法:
double p=k*log10(n);
p=p-(int)p;
int ans=(int)(pow(10,p)*100);
这就是前三位。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
ll mpow(ll a,ll n){
int ans=1;
while(n>0){
if(n%2==1)
ans = ans*a%1000;
a = a*a%1000;
n>>=1;
}
return ans;
}
double change(double a){
while(a>=1000.0){
a/=10.0;
}
return a;
}
double dpow(double a,int n){//模拟快速幂求前3位
double ans =1.0;
while(n>0){
if(n%2==1){
ans = change(ans*a);
}
a = change(a*a);
n>>=1;
}
return ans;
}
int main(){
int T,cnt=1;
ll n,k;
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&n,&k);
double ans1 = dpow(n,k);
int ans2 = mpow(n,k);
printf("Case %d: %d %03d\n",cnt,(int)ans1,ans2);
cnt++;
}
return 0;
}
LightOJ 1259 Goldbach`s Conjecture
哥德巴赫猜想是数论和所有数学中最古老的未解决问题之一。它指出:
每个偶数整数,大于2,可以表示为两个素数之和[1]。
现在你的任务是检查这个猜想是否适用于最多 的整数。
输入
输入以整数T(≤300)开始,表示测试用例的数量。
每种情况下,用包含整数的行开始N(4≤N≤10 7,n是偶数)。
输出
对于每个case,打印案例编号和表示n的方式的数量,作为两个素数的总和。更具体地讲,我们希望找到的数量(A,B) ,其中
1) 两个a和b是质数
2) a + b = n
3) a≤b
样本输入
2
6
4
样本输出
Case 1: 1
Case 2: 1
注意
1. 一个整数,被认为是首要的,如果是正好两个不同的整数整除。前几个素数是2个,3个,5个,7个,11个,13个......
很无语,发现自己另外个代码的编译器超内存,但oj却AC了?素数筛选法
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e7 + 5 ;
int prime[1000005];//这里数组大小要注意,小了会RE大了会MLE
bool Isprime[N];
int k;
void Prime() {
k = 0;
memset(Isprime, true, sizeof(Isprime));
Isprime[1] = false;
for(int i = 2 ; i < N ; i++) {
if(Isprime[i]) {
prime[k++] = i;
for(int j = 2*i ; j < N ; j+=i)
Isprime[j] = false;
}
}
}
int main() {
int t, n, x = 0;
scanf("%d", &t);
Prime();
while(t--) {
x++;
int num = 0;
scanf("%d", &n);
for(int i = 0 ; i < k && prime[i] <= n / 2 ; i++) {
if(prime[i] <= n - prime[i] && Isprime[n - prime[i]])
num++;
}
printf("Case %d: %d\n", x, num);
}
return 0;
}
LightOJ 1245 Harmonic Number (II)
我试图解决问题'1234 - 谐波数',我写了下面的代码
long long H( int n ) {
long long res = 0;
for( int i = 1; i <= n; i++ )
res = res + n / i;
return res;
}
是的,我的错误是我只使用整数除法。但是,给你n,你必须在我的代码中找到H(n)。
输入
输入以整数T(≤1000)开始,表示测试用例的数量。
每个案例都以包含整数n(1≤n<2 31)的行开头。
产量
对于每种情况,打印案例编号和代码计算的H(n)。
样本输入
11
1
2
3
4
5
6
7
8
9
10
2147483647
样本输出
Case 1: 1
Case 2: 3
Case 3: 5
Case 4: 8
Case 5: 10
Case 6: 14
Case 7: 16
Case 8: 20
Case 9: 23
Case 10: 27
Case 11: 46475828386