这一次主要是数论专题,感到思维量比上一次的数学题要多多了。同样的问题也是英文题看起来有些吃力。
UVaOJ 575 这应该算不上是一个数论题,它重新定义了一种进制转换的公式,然后根据公式计算即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
using namespace std ;
int Pow ( int x , int y );
int main ()
{
string x ;
while ( cin >> x )
{
if ( x == "0" ) { break ; }
int ans = 0 ;
for ( int i = 0 ; i < x . length (); i ++ )
{ ans += ( x [ i ] - '0' ) * ( Pow ( 2 , x . length () - i ) - 1 ); }
cout << ans << endl ;
}
return 0 ;
}
int Pow ( int x , int y )
{
int ret = 1 ;
for ( int i = 1 ; i <= y ; i ++ )
{ ret *= x ; }
return ret ;
}
Copy UVaOJ 10110 这是一道典型的数论题,最后亮着的灯,它的开关一定被拨动了奇数次。所以,我们只要看它的因数个数的奇偶性。
记得高中数学竞赛的时候遇到过类似的题目,有一个结论——完全平方数的因数有奇数个,非完全平方数的因数有偶数个。
这个命题的证明有些繁琐,读者自己思考一下就会发现,这个结论是正确的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <math.h>
using namespace std ;
int main ()
{
long long N ;
while ( cin >> N )
{
if ( N == 0 ) { break ; }
long long x = ( long long ) floor ( sqrt ( N * 1.0 ) + 0.5 );
if ( x * x == N ) { cout << "yes" << endl ; }
else { cout << "no" << endl ; }
}
return 0 ;
}
Copy UVaOJ 550 类似倒推的思想。
首先,我们有B B B 、D D D 、M M M ——分别代表进制、最后一位数字、乘数。我们不妨记满足条件的数字为 N N N ,且 N ⋅ M = S N \cdot M = S N ⋅ M = S 。
有了上述假设,我们就可以进行如下操作:
N N N 的最后一位数字为 D D D ,那么 S S S 的最后一位数字记为 T = D ⋅ M m o d B T = D \cdot M \mod B T = D ⋅ M mod B (这里为 B B B 进制)。
这时候需要注意的是,S S S 的最后一位数字,恰好是 N N N 的倒数第二位数字,那么也就是说 N N N 的倒数第二位数字为 T T T 。
有了 N N N 的倒数第二位数字,我们就可以求出 S S S 的倒数第二位数字为 T ⋅ M m o d B + T / B T\cdot M \mod B + T / B T ⋅ M mod B + T / B (其中 T / B T / B T / B 为进位),以此类推。
直到我们计算出来的数字与一开始给定的数字 D D D 相当,这时候的 N N N 的位数即为答案。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std ;
int main ()
{
int B , D , M ;
while ( cin >> B >> D >> M )
{
int nCnt = 1 ;
int nD = D ;
while (( nD = M * ( nD % B ) + ( nD / B )) && nD != D )
{ nCnt ++ ; }
cout << nCnt << endl ;
}
return 0 ;
}
Copy UVaOJ 568 由于数据范围比较小,我们可以直接算出来,记得在每次计算的时候把末尾的 0 去掉,同时为了防止溢出,我们可以对 10 的一个幂次取模。
这里选择了 10000,因为最大的 N N N 为 10000。但是这个选取并不是任意的,比如我们取 10,计算出来的结果就会出错。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <iomanip>
using namespace std ;
const int MAX = 100000 ;
int main ()
{
int N ;
while ( cin >> N )
{
int ans = 1 ;
for ( int i = 1 ; i <= N ; i ++ )
{
ans *= i ;
while ( ans % 10 == 0 ) { ans /= 10 ; }
ans %= MAX ;
}
cout << setw ( 5 ) << N << " -> " << ans % 10 << endl ;
}
}
Copy UVaOJ 408 这道题目就是求满足 s e e d ( x + 1 ) = [ s e e d ( x ) + S T E P ] m o d M O D \mathrm{seed}(x + 1) = [\mathrm{seed}(x) + \mathrm{STEP}] \mod \mathrm{MOD} seed ( x + 1 ) = [ seed ( x ) + STEP ] mod MOD 的 s e e d ( x ) \mathrm{seed}(x) seed ( x ) 能否组成模 M O D \mathrm{MOD} MOD 的剩余系。
有一个结论,若 g c d ( S T E P , M O D ) = 1 \mathrm{gcd}(\mathrm{STEP}, \mathrm{MOD}) = 1 gcd ( STEP , MOD ) = 1 ,则可以组成一个模 M O D \mathrm{MOD} MOD 的剩余系。
我们可以这样来证明:
s e e d ( x + 1 ) = [ s e e d ( x ) + S T E P ] m o d M O D = [ s e e d ( x − 1 ) + 2 ⋅ S T E P ] m o d M O D = [ s e e d ( x − 2 ) + 3 ⋅ S T E P ] m o d M O D = ⋯ = [ s e e d ( 0 ) + ( x + 1 ) ⋅ S T E P ] m o d M O D \begin{align*}\mathrm{seed}(x + 1) &= [\mathrm{seed}(x) + \mathrm{STEP}] \mod \mathrm{MOD} \\ &= [\mathrm{seed}(x - 1) + 2 \cdot \mathrm{STEP}] \mod \mathrm{MOD} \\ &= [\mathrm{seed}(x - 2) + 3 \cdot \mathrm{STEP}] \mod \mathrm{MOD} \\ &= \cdots \\ &= [\mathrm{seed}(0) + (x + 1) \cdot \mathrm{STEP}] \mod \mathrm{MOD}\end{align*} seed ( x + 1 ) = [ seed ( x ) + STEP ] mod MOD = [ seed ( x − 1 ) + 2 ⋅ STEP ] mod MOD = [ seed ( x − 2 ) + 3 ⋅ STEP ] mod MOD = ⋯ = [ seed ( 0 ) + ( x + 1 ) ⋅ STEP ] mod MOD
若不能满足条件,则必定存在 0 ≤ i ≠ j < M O D 0 \leq i \neq j < \mathrm{MOD} 0 ≤ i = j < MOD 的整数 i i i 和 j j j 满足:s e e d ( i ) = s e e d ( j ) \mathrm{seed}(i) = \mathrm{seed}(j) seed ( i ) = seed ( j ) ,即 [ s e e d ( 0 ) + i ⋅ S T E P ] m o d M O D = [ s e e d ( 0 ) + j ⋅ S T E P ] m o d M O D [\mathrm{seed}(0) + i \cdot \mathrm{STEP}] \mod \mathrm{MOD} = [\mathrm{seed}(0) +j \cdot \mathrm{STEP}] \mod \mathrm{MOD} [ seed ( 0 ) + i ⋅ STEP ] mod MOD = [ seed ( 0 ) + j ⋅ STEP ] mod MOD 即 ( ( j − i ) ⋅ S T E P ) m o d M O D = 0 ((j - i) \cdot \mathrm{STEP}) \mod \mathrm{MOD} = 0 (( j − i ) ⋅ STEP ) mod MOD = 0 又 j − i < M O D j - i < \mathrm{MOD} j − i < MOD ,所以 g c d ( S T E P , M O D ) ≠ 1 \mathrm{gcd}(\mathrm{STEP}, \mathrm{MOD}) \neq 1 gcd ( STEP , MOD ) = 1 ,即 S T E P \mathrm{STEP} STEP 中含有 M O D \mathrm{MOD} MOD 的因子——若 M O D \mathrm{MOD} MOD 整除 j − i j - i j − i ,那么 S T E P \mathrm{STEP} STEP 中含有 M O D \mathrm{MOD} MOD 的因子 M O D / ( j − i ) \mathrm{MOD} / (j - i) MOD / ( j − i ) ,反之,S T E P \mathrm{STEP} STEP 含有因子 M O D \mathrm{MOD} MOD 。
故需满足题设条件,必有 g c d ( S T E P , M O D ) = 1 \mathrm{gcd}(\mathrm{STEP}, \mathrm{MOD}) = 1 gcd ( STEP , MOD ) = 1
证毕。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <iomanip>
using namespace std ;
int gcd ( int x , int y );
int main ()
{
int x , y ;
while ( cin >> x >> y )
{
if ( gcd ( x , y ) == 1 ) { cout << setw ( 10 ) << x << setw ( 10 ) << y << " Good Choice" << endl ; }
else { cout << setw ( 10 ) << x << setw ( 10 ) << y << " Bad Choice" << endl ; }
cout << endl ;
}
return 0 ;
}
int gcd ( int x , int y )
{
if ( y == 0 ) { return x ; }
else { return gcd ( y , x % y ); }
}
Copy UVaOJ 350 简单模拟,没有想到数论的做法。不断的计算 L L L ,直到发现重复。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <memory.h>
using namespace std ;
const int MAX = 10240 ;
int f [ MAX ];
int main ()
{
int nCase = 0 ;
int Z , I , M , L ;
while ( cin >> Z >> I >> M >> L )
{
if ( Z == 0 && I == 0 && M == 0 && L == 0 ) { break ; }
int nCnt = - 1 ;
memset ( f , 0 , sizeof ( f ));
do
{
L = (( Z % M ) * ( L % M ) + ( I % M )) % M ;
f [ L ] ++ ;
nCnt ++ ;
}
while ( f [ L ] == 1 );
cout << "Case " << ++ nCase << ": " << nCnt << endl ;
}
return 0 ;
}
Copy UVaOJ 10061 要求 N ! N! N ! 转化为 B B B 进制后的位数以及末尾 0 的个数。
为了简化问题,我们首先来看 10 进制的情况。
我们知道,在 10 进制情况下,我们有 f ( N ) = f ( N / 5 ) + N / 5 f(N) = f(N / 5) + N / 5 f ( N ) = f ( N /5 ) + N /5
证明:令 k = N / 5 k = N / 5 k = N /5 ,则 N ! = 5 k ⋅ 5 ( k − 1 ) ⋅ ⋯ ⋅ 10 ⋅ 5 ⋅ a = 5 k ⋅ k ! ⋅ a N! = 5k \cdot 5(k - 1) \cdot \cdots \cdot 10 \cdot 5 \cdot a = 5^k \cdot k! \cdot a N ! = 5 k ⋅ 5 ( k − 1 ) ⋅ ⋯ ⋅ 10 ⋅ 5 ⋅ a = 5 k ⋅ k ! ⋅ a 其中 a a a 为不能被 5 整除的部分。因此, f ( N ) = f ( k ) + k = f ( N / 5 ) + N / 5 f(N) = f(k) + k = f(N / 5) + N / 5 f ( N ) = f ( k ) + k = f ( N /5 ) + N /5 证毕。
有了上面的结论,我们可以大胆猜测,对于 B B B 进制数,我们有 f ( N , B ) = f ( N / X , B ) + N / X f(N, B) = f(N / X, B) + N / X f ( N , B ) = f ( N / X , B ) + N / X 其中 X X X 为 B B B 的最大因数,证略。
这样,我们就解决了第一个问题,对于第二个问题,我们可以直接取对数,计算 log ( N ! ) \log{\left(N!\right)} log ( N ! ) ,即 log 1 + log 2 + log 3 + ⋯ + log N \log{1} + \log{2} + \log{3} + \cdots + \log{N} log 1 + log 2 + log 3 + ⋯ + log N
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <math.h>
using namespace std ;
const int MAX = 1 << 20 ;
double pLog [ MAX ];
int main ()
{
for ( int i = 1 ; i < MAX ; i ++ )
{ pLog [ i ] = pLog [ i - 1 ] + log ( i ); }
int N , B ;
while ( cin >> N >> B )
{
int nLen = ( int ) floor ( pLog [ N ] / log ( B ) + 1E-9 ) + 1 ;
int nMaxFactor , nFactorCnt = 0 , nCnt = 0 ;
for ( int i = 2 ; i <= B ; i ++ )
{
nFactorCnt = 0 ;
while ( B % i == 0 )
{
nMaxFactor = i ;
nFactorCnt ++ ;
B /= i ;
}
}
while ( N )
{
N /= nMaxFactor ;
nCnt += N ;
}
cout << nCnt / nFactorCnt << " " << nLen << endl ;
}
return 0 ;
}
Copy UVaOJ 10392 质因数分解即可,要注意题目中说只有一个大于 1,000,000 的因子,但是还是需要处理本身就是素数以及除到最后不等于 1 的情况。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <memory.h>
#include <vector>
using namespace std ;
const int MAX = 1000020 ;
vector < int > pVec ;
bool pVisited [ MAX ];
int main ()
{
memset ( pVisited , false , sizeof ( pVisited ));
for ( int i = 2 ; i < MAX ; i ++ )
{
if ( ! pVisited [ i ]) { pVec . push_back ( i ); }
for ( int j = i + i ; j < MAX ; j += i )
{ pVisited [ j ] = true ; }
}
long long N ;
while ( cin >> N )
{
if ( N < 0 ) { break ; }
int nCnt = 0 ;
for ( int i = 0 ; i < pVec . size (); i ++ )
{
if ( N < pVec [ i ]) { break ; }
while ( N % pVec [ i ] == 0 )
{
cout << " " << pVec [ i ] << endl ;
N /= pVec [ i ];
nCnt ++ ;
}
}
if ( nCnt == 0 || N != 1 ) { cout << " " << N << endl ; }
cout << endl ;
}
return 0 ;
}
Copy UVaOJ 10879 这道题目的样例好像有点吓唬人,其实里面应该有 spj 的。
数据量不大,直接分解因式即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std ;
int main ()
{
int T , N ;
cin >> T ;
for ( int i = 1 ; i <= T ; i ++ )
{
cin >> N ;
cout << "Case #" << i << ": " << N ;
int nCnt = 0 ;
for ( int j = 2 ; j * j <= N ; j ++ )
{
if ( N % j == 0 )
{
cout << " = " << j << " * " << N / j ;
nCnt ++ ;
}
if ( nCnt == 2 ) { break ; }
}
cout << endl ;
}
return 0 ;
}
Copy 数论专题很多都是看着题解做的,因为很多都不知道,学到了很多新的东西。