19级HPU算法协会公开课第三期: 【基础数学】 题解

比赛链接:传送门

A - Bi-shoe and Phi-shoe (LightOJ - 1370)

英文题*1:Bamboo Pole-vault is a massively popular sport in Xzhiland. And Master Phi-shoe is a very popular coach for his success.

在Xzhiland,竹竿跳高是一项非常受欢迎的运动。而Phi-shoe大师是一位非常受欢迎的成功教练。他需要一些竹子给他的学生,所以他让他的助手Bi-Shoe去市场买。在市场上有售的很多竹子都有可能是整数长度(实际上一定是整数 )。根据Xzhila的传统,
竹子的分数=Φ(竹子的长度)
(Xzhilans很喜欢数论)。据你所知,Φ(n)=小于n的个数,这些数是相对素数(除1之外没有公约数)到n的。因此,长度为9的竹子的分数是6,因为1、2、4、5、7、8是相对素数到9的。
Bi-shoe助理必须为每个学生买一根竹子。作为一个转折点,每个Phi-shoe的撑竿跳学生有一个幸运数字。Bi-shoe想买竹子,这样每个人都能得到一根分数大于或等于他/她的幸运数字的竹子。Bi-shoe想把买竹子的钱减到最少。一个单位的竹子要花1个Xukha。帮帮他。
Input
输入以整数T(≤100)开始,表示测试用例的数量。
每种情况都以一行开始,该行包含一个整数 n(1≤n≤10000),表示Phi-shoe的学生人数。下一行包含n个空格分隔的整数,表示学生的幸运数字。每个幸运数字都在[1,106]范围内。
Output
对于每个案例,打印案例编号和购买竹子可能花费的最低金额。详见样例。

Sample InputSample Output
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

分析

由题可知:Φ(n)是 <n 的正整数中与n互质( 公约数只有1 )的数的数目,这与 欧拉函数的定义相近。

  • 欧拉函数的定义
    在数论,对正整数n,欧拉函数是 ≤n 的正整数中与n互质的数的数目(因此φ(1)=1)。
  • 欧拉函数的公式:(两种写法长得乍一看不一样,实际上是一样的)
    • 写法①:
      φ ( x ) = x ∏ i = 1 n ( 1 − 1 p i ) φ(x)=x\prod_{i=1}^{n}(1-\frac{1}{p_i}) φ(x)=xi=1n(1pi1)(其中 p 1 , p 2 … p n p_1, p_2\dots p_n p1,p2pn x x x 的所有质因数, x x x 是不为0的整数)。
    • 写法②:
      对于任意整数 n n n ,若其质因数分解结果为 n = p 1 k 1 p 2 k 2 . . . p n k n n = p_1^{k_1}p_2^{k_2} ... p_n^{k_n} n=p1k1p2k2...pnkn ,则欧拉函数公式为 φ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) … ( 1 − 1 p n ) φ(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})\dots(1-\frac{1}{p_n}) φ(n)=n(1p11)(1p21)(1pn1) .
  • 欧拉函数的性质
    • 对于任意质数 n n n φ ( n ) = n − 1 φ(n) = n-1 φ(n)=n1 .
    • n = p k n=p^k n=pk 其中 p 为质数,则 φ ( n ) = p k − p k − 1 = ( p − 1 ) p k − 1 φ(n) = p^{k}-p^ {k-1}=(p−1)p^{k−1} φ(n)=pkpk1=(p1)pk1 .
    • g c d ( m , n ) = 1 gcd(m,n)=1 gcd(m,n)=1 时, φ ( m n ) = φ ( m ) φ ( n ) φ(mn) = φ(m)φ(n) φ(mn)=φ(m)φ(n) .
    • 如果 i i i m o d mod mod p = 0 p=0 p=0 ,其中 p p p 为质数,则 φ ( i p ) = p φ ( i ) φ(ip)=pφ(i) φ(ip)=pφ(i) .
    • 如果 i i i m o d mod mod p ≠ 0 p≠0 p=0 ,其中 p p p 为质数,则 φ ( i p ) = ( p − 1 ) φ ( i ) φ(ip)=(p−1)φ(i) φ(ip)=(p1)φ(i) .

筛法求欧拉函数的模板

bool isprime[N];	//判断第i个数是不是素数
int prime[N/10],phi[N];	//将素数存到数组prime中,phi[i]为i的欧拉函数
int m;

void euler()	//筛法求欧拉函数
{
	phi[1]=1;	//1的欧拉函数是1
	for(int i=2;i<=N;i++)
	{
		if(!isprime[i])	//i是素数
		{
			prime[++m]=i;	//由于i是素数,将i记录起来
			phi[i]=i-1;	//性质①:对于任意质数n,φ(n)=n−1.
		}
		for(int j=1;j<=m && i*prime[j]<=N;j++)
		{
			isprime[i*prime[j]]=true;	//i*其他素数而得到的数一定不是素数
			if(i%prime[j]==0)	//性质④:如果i mod p=0,其中p为质数,则φ(ip)=pφ(i).
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);	//性质⑤:如果i mod p≠0,其中p为质数,则φ(ip)=(p−1)φ(i).
		}
	}
}

根据题目中解释的竹子的分数的计算方法,可以发现与欧拉函数的区别仅在一个“等号”,而这一点就让幸运数字是 1 的同学整个人尬住。在欧拉函数中,φ(1)=1,但这题要求的是找 <n (对1来说是<1)的正整数中与n互质的数的数目,这就意味着不能从1开始找,而应该从2开始找(φ(2)=1)。

对幸运数字是 1 的同学来说,竹子的长度要从1+1=2开始找,那么其他的幸运数字 t ,是否也是满足要从t+1开始找的情况呢?
我们举例来看看:
幸运数字是 2 的时候,φ(2)=1,肯定不能从2开始找,而φ(3)=2(<3且与3互质的数有1、2,共2个)(或是用公式计算: φ ( 3 ) = 3 × ( 1 − 1 3 ) = 2 φ(3)=3×(1-\frac{1}{3})=2 φ(3)=3×(131)=2 ),这满足了题目要求的分数大于或等于他/她的幸运数字(这里是等于)。也就是说幸运数字是2的要从2+1=3开始找,满足要从t+1开始找的情况。
幸运数字是 3 的时候,φ(3)=2,φ(4)=2,φ(5)=4。要到 5 的时候才能找到大于他/她的幸运数字的竹子长度,这就意味着猜测的规律不正确吗?其实不是的,因为我们找的规律是“从t+1开始找满足条件的情况”,它自然可以从3+1=4的情况开始遍历,直到找到满足条件的就行了。

而分析欧拉函数的情况可以发现:除了φ(1)=1,其他正整数 x 的欧拉函数值 φ(x) ,都比 x 小(这里不过多解释了)。
意思就是说:对于幸运数字 t ,从φ(t+1)开始判断是没有问题的,要注意的实际上是上界问题,需要把上界弄得大一些。

代码

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e6+7;

int T,n,t,cnt;
ll sum;

bool isprime[N];	//判断第i个数是不是素数
int prime[N/10],phi[N];	//将素数存到数组prime中,phi[i]为i的欧拉函数
int m;

void euler()	//筛法求欧拉函数
{
	phi[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!isprime[i])
		{
			prime[++m]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=m && i*prime[j]<=N;j++)
		{
			isprime[i*prime[j]]=true;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}

int main()
{
	scanf("%d",&T);
	cnt=1;
	euler();
	while(T--)	//有T组数据
	{
		scanf("%d",&n);
		sum=0;
		for(int i=0;i<n;i++)	//有n个学生
		{
			scanf("%d",&t);	//第i个学生的幸运数字是t
			for(int j=t+1;j<N;j++)	//注意j的初始值是t+1
			{
				if(phi[j]>=t)	//找分数≥该学生幸运数字t的竹子j
				{
					sum=sum+j;
					break;	//找到了就完事儿
				}
			}
		}
		printf("Case %d: %lld Xukha\n",cnt,sum);
		cnt++;
	}
}

B - Aladdin and the Flying Carpet (LightOJ - 1341)

英文题*2:It’s said that Aladdin had to solve seven mysteries before getting the Magical Lamp which summons a powerful Genie. Here we are concerned about the first mystery.

据说阿拉丁在得到召唤强大精灵的神灯之前,必须先解开七个谜团。我们关心的是第一个谜团。
阿拉丁正要进入一个神奇的洞穴,在那个伪装成阿拉丁叔叔的邪恶巫师的带领下,在入口处发现了一块奇怪的神奇飞毯。有一些奇怪的生物守卫着洞口。阿拉丁可以逃跑,但他知道被抓住的可能性很大。所以,他决定利用神奇的飞毯。地毯是长方形的,但不是正方形的。 阿拉丁拿起地毯,借助它走过了入口。
现在你得到了地毯的面积和地毯最小可能边的长度,你的任务是找出有多少种类型的地毯是可能的。例如,地毯12的面积,地毯的最小可能边是2,那么可以有两种类型的地毯,它们的边是:{2,6} 和 {3,4} 。
Input
输入以整数 T(≤4000)开始,表示测试用例的数量。
每种情况都以包含两个整数的行开始:a b(1≤b≤a≤1012),其中a表示地毯的面积,b表示地毯的最小可能边。
Output
对于每个用例,打印用例编号和可能的地毯数量。

Sample InputSample Output
2
10 2
12 2
Case 1: 1
Case 2: 2

分析

这题的题意不难理解,但是需要知道的数论知识也有不少:

  • 唯一分解定理(也称 算术基本定理(上题其实也提到了)
    任意一个大于1的正整数都能表示成若干个质数的乘积,且表示的方法是唯一的。换句话说,一个数能被唯一地分解成质因数的乘积。
    公式: n = p 1 a 1 ⋅ p 2 a 2 ⋅ … ⋅ p k a k , ( p 1 < p 2 < p 3 < ⋯ < p k , a i > 0 ) n = p_1^{a_1}·p_2^{a_2} · \dots ·p_k^{a_k},(p_1<p_2<p_3<\dots<p_k,a_i>0) n=p1a1p2a2pkak,(p1<p2<p3<<pk,ai>0) .
    • 求因子数
      C o u ( n ) = ( a 1 + 1 ) ⋅ ( a 2 + 1 ) ⋅ … ⋅ ( a k + 1 ) = ∏ j = 1 k ( a j + 1 ) Cou(n) = (a_1+1)·(a_2+1) · \dots ·(a_k+1)=\prod_{j=1}^{k}(a_j+1) Cou(n)=(a1+1)(a2+1)(ak+1)=j=1k(aj+1) .

      乘法原理:做一件事,完成它需要分成 n n n 个步骤,做第一步有 m 1 m_1 m1 种不同的方法,做第二步有 m 2 m_2 m2 种不同的方法,……,做第 n n n 步有 m n m_n mn 种不同的方法。那么完成这件事共有 N = m 1 × m 2 × m 3 × ⋯ × m n N=m_1×m_2×m_3×\dots×m_n N=m1×m2×m3××mn 种不同的方法。
      p 1 p_1 p1 可以取的个数为 [ 0 , a 1 ] [0, a_1] [0,a1] p 2 p_2 p2 可以取的个数为 [ 0 , a 2 ] [0, a_2] [0,a2] p k p_k pk 可以取的个数为 [ 0 , a k ] [0, a_k] [0,ak]。根据乘法原理,总的因子个数就是这些指数 +1 的连乘,即 ( 1 + a 1 ) ⋅ ( 1 + a 2 ) ⋅ … ⋅ ( 1 + a k ) (1 + a_1)·(1 + a_2)·\dots·(1 + a_k) (1+a1)(1+a2)(1+ak)

    • 求因子和
      S u m ( n ) = ( p 1 0 + p 1 1 + p 1 2 + ⋯ + p 1 a 1 ) ⋅ ( p 2 0 + p 2 1 + p 2 2 + ⋯ + p 2 a 2 ) ⋅ … ⋅ ( p k 0 + p k 1 + p k 2 + ⋯ + p k a k ) Sum(n)=(p_1^0+p_1^1+p_1^2+\dots+p_1^{a_1})·(p_2^0+p_2^1+p_2^2+\dots+p_2^{a_2})·\dots·(p_k^0+p_k^1+p_k^2+\dots+p_k^{a_k}) Sum(n)=(p10+p11+p12++p1a1)(p20+p21+p22++p2a2)(pk0+pk1+pk2++pkak) .
      而根据等比数列求和公式 S n = a 1 ( 1 − q n ) 1 − q = a 1 − a n × q 1 − q S_n=\frac{a_1(1-q^n)}{1-q}=\frac{a_1-a_n×q}{1-q} Sn=1qa1(1qn)=1qa1an×q,上式继续推导可得:
      S u m ( n ) = p 1 a 1 + 1 − 1 p 1 − 1 ⋅ p 2 a 2 + 1 − 1 p 2 − 1 ⋅ … ⋅ p k a k + 1 − 1 p k − 1 = ∏ j = 1 k ( p j a j + 1 − 1 p j − 1 ) Sum(n)=\frac{p_1^{a_1+1}-1}{p_1-1}·\frac{p_2^{a_2+1}-1}{p_2-1}·\dots·\frac{p_k^{a_k+1}-1}{p_k-1}=\prod_{j=1}^{k}(\frac{p_j^{a_j+1}-1}{p_j-1}) Sum(n)=p11p1a1+11p21p2a2+11pk1pkak+11=j=1k(pj1pjaj+11) .

欧拉筛法(线性筛)的模板

bool isprime[N];
int prime[N/10];
int m;

void primes()	//欧拉筛(线性筛)
{
	memset(isprime,false,sizeof(isprime));
	for(int i=2;i<N;i++)
	{
		if(!isprime[i])	prime[++m]=i;
		for(int j=1;j<=m && i*prime[j]<N;j++)
		{
			isprime[i*prime[j]]=true;
			if(i%prime[j]==0)	break;	//保证每个合数只会被它最小质因子筛掉
		}
	}
}

求因子数的模板

ll solve(ll n)	//求因子数
{
	ll num,ans=1;
	for(ll i=1;i<=m && prime[i]*prime[i]<=n;i++)
	{
		num=0;
		while(n%prime[i]==0)
		{
			num++;
			n/=prime[i];
		}
		ans*=(num+1);
	}
	if(n!=1)	ans*=2;
	return ans;
}

根据唯一分解定理,先将 a a a 进行分解,那么可以得到 a a a 的所有因子的个数 s u m = ( 1 + a 1 ) ⋅ ( 1 + a 2 ) ⋅ … ⋅ ( 1 + a n ) sum=(1+a_1)·(1+a_2)·\dots·(1+a_n) sum=(1+a1)(1+a2)(1+an)。如果 a = x ⋅ y a=x·y a=xy ,根据题目中的“地毯是长方形的,但不是正方形的”,可以知道不会出现 x = y x=y x=y 的情况。而 ( x , y ) (x,y) (x,y) ( y , x ) (y,x) (y,x) 其实是同一种情况,因此算出 a a a 的因子数后应该把 s u m sum sum 再除以 2 2 2 ,去掉重复的情况。由于 a a a 的最小因子是 b b b ,因此枚举一下,减去 < b <b b 的正整数中 a a a 的约数即可。

代码

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e6+7;

int T,cnt;
ll a,b,sum;

bool isprime[N];
int prime[N/10];
int m;

void primes()	//欧拉筛(线性筛)
{
	memset(isprime,false,sizeof(isprime));
	for(int i=2;i<N;i++)
	{
		if(!isprime[i])	prime[++m]=i;
		for(int j=1;j<=m && i*prime[j]<N;j++)
		{
			isprime[i*prime[j]]=true;
			if(i%prime[j]==0)	break;
		}
	}
}

ll solve(ll n)	//求因子数
{
	ll num,ans=1;
	for(ll i=1;i<=m && prime[i]*prime[i]<=n;i++)
	{
		num=0;
		while(n%prime[i]==0)
		{
			num++;
			n/=prime[i];
		}
		ans*=(num+1);
	}
	if(n!=1)	ans*=2;
	return ans;
}

int main()
{
	scanf("%d",&T);
	cnt=1;
	primes();
	while(T--)	//有T组测试用例
	{
		scanf("%lld%lld",&a,&b);	//a表示地毯的面积,b表示地毯的最小可能边
		sum=0;
		if(b*b>a)	//边是b的正方形面积都比a大,就不可能找到最小边是b且面积是a的长方形了
			printf("Case %d: %lld\n",cnt,0);
		else
		{
			sum=solve(a);	//sum等于a的因子数
			sum/=2;	//去掉重复情况
			for(int i=1;i<b;i++)	//减去<b的正整数中a的约数
			{
				if(a%i==0)
				sum--;
			}
			printf("Case %d: %lld\n",cnt,sum);
		}
		cnt++;
	}
}

C - Goldbach`s Conjecture (LightOJ - 1259)

英文题*3:Goldbach’s conjecture is one of the oldest unsolved problems in number theory and in all of mathematics.

哥德巴赫猜想是数论和数学中最古老的未解问题之一。它指出:
大于2的偶数整数可以表示为两个素数之和 [1]。
现在你的任务是检查这个猜想是否适用于107以下的整数。
Input
输入以整数 T (≤300) 开始,表示测试用例的数量。
每种情况都以包含整数 n (4≤n≤107,n为偶数) 的行开始。
Output
对于每种情况,打印案例号和可以表示n的两个素数之和的方式数。更具体地说,我们想找出(a,b)的个数,要求:
1) a和b都是质数
2) a+b=n
3) a≤b
Note
1.如果一个整数可以被两个完全不同的整数整除,则称之为素数。前几个素数是2, 3, 5, 7, 11, 13, …

Sample InputSample Output
2
6
4
Case 1: 1
Case 2: 1

分析

这题的题意也挺好理解的,这里用到了埃氏筛法筛素数。
埃氏筛法:当我们知道一个数为素数时,它的倍数一定不是素数,因此可以筛掉它所有的合数。
埃氏筛法模板

bool isprime[N];
int prime[N/10];
int m;

void primes()	//埃氏筛法
{
	memset(isprime,false,sizeof(isprime));
	for(ll i=2;i<N;i++)
	{
		if(!isprime[i])
		{
			prime[++m]=i;
			for(ll j=i*i;j<N;j+=i)	isprime[j]=true;
		}
	}	
}

代码

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e7;

int T,n,cnt,sum;

bool isprime[N];
int prime[N/10];
int m;

void primes()	//埃氏筛法
{
	memset(isprime,false,sizeof(isprime));
	for(ll i=2;i<N;i++)
	{
		if(!isprime[i])
		{
			prime[++m]=i;
			for(ll j=i*i;j<N;j+=i)	isprime[j]=true;
		}
	}	
}

int main()
{
	scanf("%d",&T);
	cnt=1;
	primes();
	while(T--)	//有T组测试用例
	{
		scanf("%d",&n);
		sum=0;
		for(int i=1;i<=m && prime[i]<=n/2;i++)	//prime[i]<=n/2也可以写成prime[i]<=n-prime[i],可能更好理解一点
		{
			if(!isprime[n-prime[i]])	//如果n-prime[i]也是素数,就说明找到了
			{
				sum++;
			}
		}
		printf("Case %d: %d\n",cnt,sum);
		cnt++;
	}
}

D - Pairs Forming LCM (LightOJ - 1236)

英文题*4:Find the result of the following code:

long long pairsFormLCM( int n ) {
    long long res = 0;
    for( int i = 1; i <= n; i++ )
        for( int j = i; j <= n; j++ )
           if( lcm(i, j) == n ) res++; // lcm means least common multiple
    return res;
}

A straight forward implementation of the code may time out. If you analyze the code, you will find that the code actually counts the number of pairs (i, j) for which lcm(i, j) = n and (i ≤ j).

代码的直接实现可能会超时。如果您分析代码,您会发现代码实际上计算了 lcm(i,j)=n 且(i≤j)的一对 (i,j) 。
Input
输入以整数 T(≤200) 开始,表示测试用例的数量。
每种情况都以包含整数 n(1≤n≤1014) 的行开始。
Output
对于每个案例,打印案例编号和函数“pairsFormLCM(n)”返回的值。

Sample InputSample Output
15
2
3
4
6
8
10
12
15
18
20
21
24
25
27
29
Case 1: 2
Case 2: 2
Case 3: 3
Case 4: 5
Case 5: 4
Case 6: 5
Case 7: 8
Case 8: 5
Case 9: 8
Case 10: 8
Case 11: 5
Case 12: 11
Case 13: 3
Case 14: 4
Case 15: 2

分析

此题的题意是:给你一个数 n n n ,计算满足 l c m ( i , j ) = n lcm(i,j)=n lcm(i,j)=n i ≤ j i≤j ij ( i , j ) (i,j) (i,j) 的个数。

根据唯一分解定理,如果 n = l c m ( a , b ) n=lcm(a,b) n=lcm(a,b) ,那么对于 n = p 1 e 1 ⋅ p 2 e 2 ⋅ … ⋅ p n e n n=p_1^{e_1}·p_2^{e_2}·\dots·p_n^{e_n} n=p1e1p2e2pnen a = p 1 a 1 ⋅ p 2 a 2 ⋅ … ⋅ p n a n a=p_1^{a_1}·p_2^{a_2}·\dots·p_n^{a_n} a=p1a1p2a2pnan b = p 1 b 1 ⋅ p 2 b 2 ⋅ … ⋅ p n b n b=p_1^{b_1}·p_2^{b_2}·\dots·p_n^{b_n} b=p1b1p2b2pnbn ,满足 n = p 1 m a x ( a 1 , b 1 ) ⋅ p 2 m a x ( a 2 , b 2 ) ⋅ … ⋅ p n m a x ( a n , b n ) n=p_1^{max(a_1,b_1)}·p_2^{max(a_2,b_2)}·\dots·p_n^{max(a_n,b_n)} n=p1max(a1,b1)p2max(a2,b2)pnmax(an,bn) 。因此,对于每个质因子 p i p_i pi p i p_i pi a a a b b b 中的指数 a i a_i ai b i b_i bi ,至少有一个与 e i e_i ei 相等,另一个 ≤ e i ≤e_i ei
那么,对于每个 p i p_i pi 来说,有以下两种情况:
① 若 a i = e i a_i=e_i ai=ei ,那么 b i b_i bi 可以是 [ 0 , e i ] [0,e_i] [0,ei] 中的任意数,共有 e i + 1 e_i+1 ei+1 种情况。
② 若 a i < e i a_i<e_i aiei ,即 a i a_i ai [ 0 , e i ) [0,e_i) [0,ei) 中的任意数,那么 b i b_i bi 必须是 e i e_i ei ,共有 e i e_i ei 种情况。
因此,对于每个质因子 p i p_i pi ,都有 2 e i + 1 2e_i+1 2ei+1 种情况,根据乘法原理,满足条件的 ( a , b ) (a,b) (a,b) 共有 ∏ ( 2 e i + 1 ) \prod(2e_i+1) (2ei+1) 个。而除了 ( n , n ) (n,n) (n,n) ,其他的情况都出现了两次,而我们只需取一次就可(要 a ≤ b a≤b ab ),因此满足 a ≤ b a≤b ab l c m ( a , b ) = n lcm(a,b)=n lcm(a,b)=n 的有 ( ∏ ( 2 e i + 1 ) 2 + 1 ) (\frac{\prod(2e_i+1)}{2}+1) (2(2ei+1)+1) 个。

这里枚举质数表用的是欧拉筛。为了防止输入的 n n n 中有比枚举的质数表还大的质因子,添加了判断 n n n 是否 ≠ 1 ≠1 =1(也可写成是否 > 1 >1 >1 )的条件,如果满足该条件,则做 a n s = 3 ∗ a n s ans=3*ans ans=3ans ,即 a n s = ( 2 ∗ 1 + 1 ) ∗ a n s ans=(2*1+1)*ans ans=(21+1)ans .

代码

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e7;

int T,cnt;
ll n,num,ans;

bool isprime[N];
int prime[N/10];
int m;

void primes()	//欧拉筛(线性筛)
{
	memset(isprime,false,sizeof(isprime));
	isprime[1]=true,m=0;
	for(ll i=2;i<N;i++)
	{
		if(!isprime[i])	prime[++m]=i;
		for(ll j=1;j<=m && i*prime[j]<N;j++)
		{
			isprime[i*prime[j]]=true;
			if(i%prime[j]==0)	break;
		}
	}	
}

int main()
{
	scanf("%d",&T);
	cnt=1;
	primes();
	while(T--)	//有T组测试用例
	{
		scanf("%lld",&n);
		ans=1;
		for(ll i=1;i<=m && prime[i]<=n;i++)
		{
			num=0;
			while(n%prime[i]==0)
			{
				num++;
				n/=prime[i];
			}
			ans=ans*(2*num+1);
		}
		if(n!=1)	ans=3*ans;	//防止n中有的质因子比枚举的质数表大
		printf("Case %d: %lld\n",cnt,ans/2+1);
		cnt++;
	}
}

F - Large Division (LightOJ - 1214)

Given two integers, a and b, you should check whether a is divisible by b or not. We know that an integer a is divisible by an integer b if and only if there exists an integer c such that a = b * c.

给定两个整数a和b,您应该检查a是否可以被b整除。我们知道只要存在一个整数c,使得a=b*c,那么整数a可以被一个整数b整除。
Input
输入以整数 T(≤525)开始,表示测试用例的数量。
每种情况都以包含两个整数 a (-10200≤a≤10200) 和 b (|b|>0,b适合32位有符号整数) 的行开始。数字不包含前导零。
Output
对于每个案例,请先打印案例编号。如果a可被b整除,则打印“divisible”。否则打印“not divisible”。

Sample InputSample Output
6
101 101
0 67
-101 101
7678123668327637674887634 101
11010000000000000000 256
-202202202202000202202202 -101
Case 1: divisible
Case 2: divisible
Case 3: divisible
Case 4: not divisible
Case 5: divisible
Case 6: divisible

分析

32位有符号整数的范围是:- 231 ~ 231 - 1。这与 i n t int int 的范围是一致的。

我们可以看到 a a a 的范围非常的大,不管是 i n t int int 或是 l o n g long long l o n g long long 都无法直接进行定义,因此选用 c h a r char char 数组来存放 a a a 的数据。
解决了存放的问题,计算又是另一个问题。这里问的是大数的整除计算,考虑使用求余的方法,这样就需要了解三大余数定理:

  • 加法定理
    a a a b b b 的和除以 c c c 的余数,等于 a a a b b b 分别除以 c c c 的余数之和,或这个和除以 c c c 的余数。
    比如:
    23÷5=4…3
    16÷5=3…1
    (23+16)÷5=7…(4=3+1)
  • 乘法定理
    a a a b b b 的积除以 c c c 的余数,等于 a a a b b b 分别除以 c c c 的余数之积,或这个积除以 c c c 的余数。
    比如:
    23÷5=4…3
    16÷5=3…1
    (23×16)÷5=73…(3=3×1)
  • 同余定理
    若两个整数 a a a b b b 被自然数 m m m 除有相同的余数,那么称 a a a b b b 对应模 m m m 同余,记为同余式 a ≡ b ( m o d m ) a≡b(mod m) ab(modm)
    由同余的性质,我们可以得到一个推论:若 a a a b b b 同余,则 a a a b b b 的差一定能被 m m m 整除,记为:如果 a ≡ b ( m o d m ) a≡b(mod m) ab(modm) ,那么一定有 a − b = m k a-b=mk ab=mk k k k 为整数。

正好我们是用 c h a r char char 类型的数组来存放 a a a 的值的,就可以从前往后遍历数组 a a a ,利用加法定理进行求余运算。
而要知道 a [ i ] a[i] a[i] 的数值,因为它是字符类型的,所以可以写成 a [ i ] − ′ 0 ′ a[i]-'0' a[i]0 把它转化成数值类型的(当然也有其他方法,这里不再过多阐述)。

要注意的是:作为存放 a a a b b b 求余结果的 s u m sum sum ,因为 b b b 的值可能是 i n t int int 最大范围的那个值,而 a a a 的值可能略微比 b b b 大一点,那么在遍历到最后一个字符时都有可能无法被 b b b 求余得到更小的值。因此如果把 s u m sum sum 定义成 i n t int int 类型的话,就有可能会出错,因此最好定义成 l o n g long long l o n g long long

a a a 能被 b b b 整除, s u m sum sum 最后的计算结果是 0 0 0 a a a 不能被 b b b 整除,则 s u m sum sum 的值不为 0 0 0 .

代码

#include<cstdio>
#include<cstring>
using namespace std;

int T,cnt,b;
char a[300];
long long sum;	//sum不能定义成int

int main()
{
	scanf("%d",&T);
	cnt=1;
	while(T--)
	{
		sum=0;
		scanf("%s%d",&a,&b);
		for(int i=0;i<strlen(a);i++)
		{
			if(a[i]=='-')	a[i]='0';	//如果遇到了负号,就把该位置变成0,或者也可以跳过该位置再做下面的运算
			sum=(sum*10+(a[i]-'0'))%b;
		}
		if(sum==0)	printf("Case %d: divisible\n",cnt);	//能被整除
		else	printf("Case %d: not divisible\n",cnt);	//不能被整除
		cnt++;
	}
}

J - Maximum GCD (UVA - 11827)

Given the N integers, you have to find the maximum GCD (greatest common divisor) of every possible pair of these integers.

给定N个整数,你必须找到每对整数的最大公因数。
Input
输入的第一行是一个整数 N (1<N<100),它决定了测试用例的数量。
下面的 N 行是 N 个测试用例。每个测试用例包含 M (1<M<100) 个正整数,您必须找到GCD的最大值。
Output
对于每个测试用例,显示每个可能的最大GCD。

Sample InputSample Output
3
10 20 30 40
7 5 12
125 15 25
20
1
25

分析

这题第一次写的时候下意识自作多情地在最后写上了:

		if(N!=0)	printf("%d\n",t);
		else	printf("%d",t);

想正经输出一下,最后一行没有换行啥的,然后就CE了,挺突然的,直接简单粗暴写printf("%d\n",t);就是可以滴。
其他就看代码吧,比较好理解。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

int N,a[107],len,sum,cnt,t;
char b[10007];

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

int main()
{
	scanf("%d",&N);
	getchar();	//吸收回车
	while(N--)
	{
		gets(b);
		len=strlen(b);
		cnt=0;
		for(int i=0;i<len;i++)
		{
			sum=0;
			while(b[i]!=' ' && i<len)
			{
				sum=10*sum+b[i]-'0';
				i++;
			}
			a[cnt++]=sum; 
		}
		t=0;
		for(int i=0;i<cnt;i++)
		{
			for(int j=i+1;j<cnt;j++)
			{
				t=max(t,gcd(a[i],a[j]));
			}
		}
		printf("%d\n",t);
	}
}

K - Primes (HDU - 2161)

Write a program to read in a list of integers and determine whether or not each number is prime. A number, n, is prime if its only divisors are 1 and n.

编写一个程序,读入一个整数列表,并确定每个数是否为素数。如果一个数n的唯一因子是1和n,那么它是素数。对于这个问题,数1和2不被认为是素数。
Input
每个输入行都包含一个整数。整数列表以<=0的数字结尾。您可以假设输入最多包含250个数字,每个数字小于或等于16000。
Output
输出应该包含每一个数字对应的一行,其中每一行首先列出问题编号,然后是冒号和空格,最后是"yes" 或 “no”。

Sample InputSample Output
1
2
3
4
5
17
0
1: no
2: no
3: yes
4: no
5: yes
6: yes

分析

这题不算难,只要每个输入都判断一下是不是素数就行了。
需要注意的有两点:①1和2在此题中不算素数;②输入结束是输入 ≤0 的数。

代码

#include<cstdio>
using namespace std;

int n,cnt;

bool prime(int x)
{
	if(x==1 || x==2)	return false;	//1和2在此题中不算素数
	for(int i=2;i*i<=x;i++)
	{
		if(x%i==0)	return false;
	}
	return true;
}

int main()
{
	cnt=1;
	while(~scanf("%d",&n))
	{
		if(n<=0)	break;	//注意是<=0,不是==0
		if(prime(n))	//n是素数
		{
			printf("%d: yes\n",cnt);
			cnt++;
		}
		else	//n不是素数
		{
			printf("%d: no\n",cnt);
			cnt++;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值