ACM-ICPC 寒假练习 06

这一次主要是数论专题,感到思维量比上一次的数学题要多多了。同样的问题也是英文题看起来有些吃力。

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;
}

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;
}

UVaOJ 550

类似倒推的思想。

首先,我们有BBDDMM ——分别代表进制、最后一位数字、乘数。我们不妨记满足条件的数字为 NN,且 NM=SN \cdot M = S

有了上述假设,我们就可以进行如下操作:

NN 的最后一位数字为 DD,那么 SS 的最后一位数字记为 T=DMmod  BT = D \cdot M \mod B(这里为 BB 进制)。

这时候需要注意的是,SS 的最后一位数字,恰好是 NN 的倒数第二位数字,那么也就是说 NN 的倒数第二位数字为 TT

有了 NN 的倒数第二位数字,我们就可以求出 SS 的倒数第二位数字为 TMmod  B+T/BT\cdot M \mod B + T / B(其中 T/BT / B 为进位),以此类推。

直到我们计算出来的数字与一开始给定的数字 DD 相当,这时候的 NN 的位数即为答案。

 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;
}

UVaOJ 568

由于数据范围比较小,我们可以直接算出来,记得在每次计算的时候把末尾的 0 去掉,同时为了防止溢出,我们可以对 10 的一个幂次取模。

这里选择了 10000,因为最大的 NN 为 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;
    }
}

UVaOJ 408

这道题目就是求满足 seed(x+1)=[seed(x)+STEP]mod  MOD\mathrm{seed}(x + 1) = [\mathrm{seed}(x) + \mathrm{STEP}] \mod \mathrm{MOD}seed(x)\mathrm{seed}(x) 能否组成模 MOD\mathrm{MOD} 的剩余系。

有一个结论,若 gcd(STEP,MOD)=1\mathrm{gcd}(\mathrm{STEP}, \mathrm{MOD}) = 1,则可以组成一个模 MOD\mathrm{MOD} 的剩余系。

我们可以这样来证明:

seed(x+1)=[seed(x)+STEP]mod  MOD=[seed(x1)+2STEP]mod  MOD=[seed(x2)+3STEP]mod  MOD==[seed(0)+(x+1)STEP]mod  MOD\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*}

若不能满足条件,则必定存在 0ij<MOD0 \leq i \neq j < \mathrm{MOD} 的整数 iijj 满足:seed(i)=seed(j)\mathrm{seed}(i) = \mathrm{seed}(j),即 [seed(0)+iSTEP]mod  MOD=[seed(0)+jSTEP]mod  MOD[\mathrm{seed}(0) + i \cdot \mathrm{STEP}] \mod \mathrm{MOD} = [\mathrm{seed}(0) +j \cdot \mathrm{STEP}] \mod \mathrm{MOD}((ji)STEP)mod  MOD=0((j - i) \cdot \mathrm{STEP}) \mod \mathrm{MOD} = 0ji<MODj - i < \mathrm{MOD},所以 gcd(STEP,MOD)1\mathrm{gcd}(\mathrm{STEP}, \mathrm{MOD}) \neq 1,即 STEP \mathrm{STEP} 中含有 MOD\mathrm{MOD} 的因子——若 MOD\mathrm{MOD} 整除 jij - i,那么 STEP\mathrm{STEP} 中含有 MOD\mathrm{MOD} 的因子 MOD/(ji)\mathrm{MOD} / (j - i),反之,STEP\mathrm{STEP} 含有因子 MOD\mathrm{MOD}

故需满足题设条件,必有 gcd(STEP,MOD)=1\mathrm{gcd}(\mathrm{STEP}, \mathrm{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); }
}

UVaOJ 350

简单模拟,没有想到数论的做法。不断的计算 LL,直到发现重复。

 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;
}

UVaOJ 10061

要求 N!N! 转化为 BB 进制后的位数以及末尾 0 的个数。

为了简化问题,我们首先来看 10 进制的情况。

我们知道,在 10 进制情况下,我们有 f(N)=f(N/5)+N/5f(N) = f(N / 5) + N / 5

证明:令 k=N/5k = N / 5,则 N!=5k5(k1)105a=5kk!aN! = 5k \cdot 5(k - 1) \cdot \cdots \cdot 10 \cdot 5 \cdot a = 5^k \cdot k! \cdot a 其中 aa 为不能被 5 整除的部分。因此, f(N)=f(k)+k=f(N/5)+N/5f(N) = f(k) + k = f(N / 5) + N / 5 证毕。

有了上面的结论,我们可以大胆猜测,对于 BB 进制数,我们有 f(N,B)=f(N/X,B)+N/Xf(N, B) = f(N / X, B) + N / X 其中 XXBB 的最大因数,证略。

这样,我们就解决了第一个问题,对于第二个问题,我们可以直接取对数,计算 log(N!)\log{\left(N!\right)},即 log1+log2+log3++logN\log{1} + \log{2} + \log{3} + \cdots + \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;
}

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;
}

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;
}

数论专题很多都是看着题解做的,因为很多都不知道,学到了很多新的东西。

使用 Hugo 构建
主题 StackJimmy 设计