AcWing算法提高课 数学知识

AcWing算法提高课 数学知识
筛质数 

1292. 哥德巴赫猜想

哥德巴赫猜想的内容如下:

任意一个大于 44 的偶数都可以拆成两个奇素数之和。

例如:

8=3+58=3+5
20=3+17=7+1320=3+17=7+13
42=5+37=11+31=13+29=19+2342=5+37=11+31=13+29=19+23

现在,你的任务是验证所有小于一百万的偶数能否满足哥德巴赫猜想。

输入格式

输入包含多组数据。

每组数据占一行,包含一个偶数 nn。

读入以 00 结束。

输出格式

对于每组数据,输出形如 n = a + b,其中 a,ba,b 是奇素数。

若有多组满足条件的 a,ba,b,输出 bab−a 最大的一组。

若无解,输出 Goldbach's conjecture is wrong.

数据范围

6n<1066≤n<106

输入样例:

8
20
42
0

输出样例:

8 = 3 + 5
20 = 3 + 17
42 = 5 + 37

线性筛

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n;
int p[N], cnt;
bool st[N];
void get_primes()
{
    st[0] = st[1] = 1;
    for (int i = 2; i <= N; i ++ )
    {
        if (!st[i]) p[cnt ++ ] = i;
        for (int j = 0; i * p[j] <= N; j ++ )
        {
            st[i * p[j]] = 1;
            if (i % p[j] == 0) break;
        }
    }
}
int main()
{
    get_primes();
    while (cin >> n, n)
    {
        for (int i = 0; i < n; i ++ ) 
            if (!st[i] && !st[n - i]) 
            {
                printf("%d = %d + %d\n", n, i, n - i);
                break;
            }
    }
    return 0;
}

1293. 夏洛克和他的女朋友

题目:

夏洛克有了一个新女友(这太不像他了!)。

情人节到了,他想送给女友一些珠宝当做礼物。

他买了 nn 件珠宝,第 ii 件的价值是 i+1i+1,也就是说,珠宝的价值分别为 2,3,,n+12,3,…,n+1。

华生挑战夏洛克,让他给这些珠宝染色,使得一件珠宝的价格是另一件珠宝的价格的质因子时,两件珠宝的颜色不同。

并且,华生要求他使用的颜色数尽可能少。

请帮助夏洛克完成这个简单的任务。

输入格式

只有一行一个整数 nn,表示珠宝件数。

输出格式

第一行一个整数 kk,表示所使用的颜色数;

第二行 nn 个整数,表示第 11 到第 nn 件珠宝被染成的颜色。

若有多种答案,输出任意一种。

请用 11 到 kk 表示你用到的颜色。

数据范围

1n1051≤n≤105

输入样例1:

3

输出样例1:

2
1 1 2

输入样例2:

4

输出样例2:

2
2 1 1 2

分析:

我们让素数是一种颜色,合数是另一种颜色

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n;
int p[N], cnt;
bool st[N];
void get_prime(int n)
{
    st[0] = 1; st[1] = 1;
    for (int i = 2; i <= n; i++ )
    {
        if (!st[i]) p[cnt ++ ] = i;
        for (int j = 0; p[j] * i <= n; j ++ ) 
        {
            st[i * p[j]] = 1;
            if (i % p[j] == 0) break;
        }
    }
}
int main()
{
    cin >> n;
    get_prime(n + 1);
    if (n > 3) cout << 2 << endl;
    else cout << 1 << endl;
    for (int i = 2; i <= n + 1; i ++ ) 
        if (!st[i]) printf("%d ", 1);
        else printf("%d ", 2);
}

  196. 质数距离

题目:

给定两个整数 LL 和 UU,你需要在闭区间 [L,U][L,U] 内找到距离最接近的两个相邻质数 C1C1 和 C2C2(即 C2C1C2−C1 是最小的),如果存在相同距离的其他相邻质数对,则输出第一对。

同时,你还需要找到距离最远的两个相邻质数 D1D1 和 D2D2(即 D1D2D1−D2 是最大的),如果存在相同距离的其他相邻质数对,则输出第一对。

输入格式

每行输入两个整数 LL 和 UU,其中 LL 和 UU 的差值不会超过 106106。

输出格式

对于每个 LL 和 UU,输出一个结果,结果占一行。

结果包括距离最近的相邻质数对和距离最远的相邻质数对。(具体格式参照样例)

如果 LL 和 UU 之间不存在质数对,则输出 There are no adjacent primes.

数据范围

1L<U23111≤L<U≤231−1

输入样例:

2 17
14 17

输出样例:

2,3 are closest, 7,11 are most distant.
There are no adjacent primes.

分析:

将L到R的合数筛出来,用st[i - L]表示

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000010;
int p[N], cnt;
bool st[N];
bool vis[N];
void init()
{
    for (int i = 2; i <= N; i ++ ) 
    {
        if (!st[i]) p[cnt ++ ] = i;
        for (int j = 0; p[j] * i <= N; j ++ )
        {
            st[i * p[j]] = 1;
            if (i % p[j] == 0) break;
        }
    }
}
int w[N];
int main()
{
    init();
    // for (int j = 0; j < cnt; j ++ ) cout << p[j] << " ";
    // cout << endl;
    int l, r;
    while (cin >> l >> r)
    {
        memset(vis, 0, sizeof vis);
        for (int i = 0; i < cnt; i ++ )
        {
            ll pp = p[i];
            for (ll j = max(2 * pp, (l + pp - 1) / pp * pp); j <= r; j += pp )
                vis[j - l] = true;
        }
        int tot = 0;
        for (int i = 0; i <= r - l;i ++ )
            if (!vis[i] && i + l >= 2)
                w[tot ++ ] = i + l;
        if (tot < 2) puts("There are no adjacent primes.");
        else 
        {
            int mi = 0, mx = 0;
            for (int i = 0; i + 1 < tot; i ++ )
            {
                int d = w[i + 1] - w[i];
                if (d < w[mi + 1] - w[mi]) mi = i;
                if (d > w[mx + 1] - w[mx]) mx = i;
            }
            printf("%d,%d are closest, %d,%d are most distant.\n", w[mi], w[mi + 1], w[mx], w[mx + 1]);
        }
        
        // for (int i = l; i <= r; i ++ ) 
        //     cout << vis[i - l] << " ";
        // cout << endl;
        // int start = l, last = l;
        // int a = 2e9, b = -2e9;
        // for (int i = l + 1; i <= r; i ++ )
        // {
        //     if (!vis[i - l] && !vis[start - l] && a > i - start + 1)
        //     {
        //         a = i - start + 1;
        //         start = i;
        //     }
        //     if (!vis[i - l] && !vis[last - l] && b < i - last + 1)
        //     {
        //         b = i - last + 1;
        //         last = i;
        //     }
        // }
        // // cout << a << endl;
        // if (a == 2e9) puts("There are no adjacent primes.");
        // else printf("%d,%d are closest, %d,%d are most distant.\n", start - a + 1, start, last - b + 1, last);
    }
}

 

分解质因数 

197. 阶乘分解

题目:

给定整数 N,试把阶乘 N! 分解质因数,按照算术基本定理的形式输出分解结果中的 pi 和 ci 即可。

输入格式

一个整数 N。

输出格式

N! 分解质因数后的结果,共若干行,每行一对 pi,ci,表示含有 pi^ci 项。按照 pi 从小到大的顺序输出。

数据范围

1≤N≤106

输入样例:

5

输出样例:

2 3
3 1
5 1

样例解释

5!=120=2335

分析:

求N!中质因子p的个数,那么我们知道在1~N中,有N / p个p的倍数,有N / (P ^ 2)个p^2个质因子。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n;
int p[N], cnt;
bool st[N];
void get_primes(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i]) p[cnt ++ ] = i;
        for (int j = 0; i * p[j] <= n; j ++ )
        {
            st[i * p[j]] = true;
            if (i % p[j] == 0) break;
        }
    }
}
int main()
{
    cin >> n;    
    get_primes(n);
    for (int i = 0; i < cnt; i ++ )
    {
        int s = 0, pp = p[i];
        for (int j = n; j; j /= pp)
        {
            s += j / pp;
        }
        printf("%d %d\n", pp, s);
    }
}

  

快速幂

1289. 序列的第k个数

题目:

BSNY 在学等差数列和等比数列,当已知前三项时,就可以知道是等差数列还是等比数列。

现在给你 整数 序列的前三项,这个序列要么是等差序列,要么是等比序列,你能求出第 k 项的值吗。

如果第 k 项的值太大,对其取模 200907。

输入格式

第一行一个整数 T,表示有 T 组测试数据;

对于每组测试数据,输入前三项 a,b,c,然后输入 k。

输出格式

对于每组数据,输出第 k 项取模 200907 的值。

数据范围

1≤T≤100,
1≤a≤b≤c≤109,
1≤k≤109

输入样例:

2
1 2 3 5
1 2 4 5

输出样例:

5
16

分析:

等差数列:a + c = 2 * b

等比数列:a * c = b * b

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 200907;
ll qmi(ll a, ll b)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1) ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
ll a, b, c, k;
int main()
{
    int T; cin >> T;
    while (T -- )
    {
        cin >> a >> b >> c >> k;
        if (a + c == b << 1)
        {
            ll d = b - a;
            cout << (a + (k - 1) * d) % mod << endl;
        }
        else 
        {
            ll q = b / a;
            ll ans = a * qmi(q, k - 1) % mod;
            cout << ans << endl;
        }
    }
}

  

1290. 越狱

题目:

监狱有连续编号为 1 到 n 的 n 个房间,每个房间关押一个犯人。

有 m 种宗教,每个犯人可能信仰其中一种。

如果相邻房间的犯人信仰的宗教相同,就可能发生越狱。

求有多少种状态可能发生越狱。

输入格式

共一行,包含两个整数 m 和 n。

输出格式

可能越狱的状态数,对 100003 取余。

数据范围

1≤m≤108,
1≤n≤1012

输入样例:

2 3

输出样例:

6

样例解释

所有可能的 6 种状态为:(000)(001)(011)(100)(110)(111)

分析:

容斥,将全集减去合法集

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 100003;
ll qmi(ll a, ll b)
{
    ll ans = 1;
    while (b)
    {
        if (b & 1) ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
ll n, m;
int main()
{
    cin >> m >> n;
    cout << (qmi(m, n) % mod - m * qmi(m - 1, n - 1) % mod + mod) % mod << endl;
}  

约数个数

1291. 轻拍牛头

题目:

今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏.

贝茜让 N 头奶牛(编号 1 到 N)坐成一个圈。

除了 1 号与 N 号奶牛外,i 号奶牛与 i−1 号和 i+1 号奶牛相邻,N 号奶牛与 1 号奶牛相邻。

农夫约翰用很多纸条装满了一个桶,每一张纸条中包含一个 1 到 1000000 之间的数字。

接着每一头奶牛 i 从桶中取出一张纸条,纸条上的数字用 Ai 表示。

所有奶牛都选取完毕后,每头奶牛轮流走上一圈,当走到一头奶牛身旁时,如果自己手中的数字能够被该奶牛手中的数字整除,则拍打该牛的头。

牛们希望你帮助他们确定,每一头奶牛需要拍打的牛的数量。

即共有 N 个整数 A1,A2,…,AN,对于每一个数 Ai,求其他的数中有多少个是它的约数。

输入格式

第一行包含整数 N。

接下来 N 行,每行包含一个整数 Ai。

输出格式

共 N 行,第 i 行的数字为第 i 头牛需要拍打的牛的数量。

数据范围

1≤N≤105,
1≤Ai≤106

输入样例:

5
2
1
2
3
4

输出样例:

2
0
2
1
3

分析:

计算每个数在n个数字中出现了多少个他的约数。

那么,我们将每个数字用cnt存起来,将所有合数在n中出现的约数都算出来,然后查表

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n;
int a[N], cnt[N];
int ans[N];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &a[i]);
        cnt[a[i]] ++;
    }
    
    for (int i = 1; i < N; i ++ ) 
        for (int j = i; j < N; j += i )
            ans[j] += cnt[i];
    for (int i = 1; i <= n; i ++ ) 
        printf("%d\n", ans[a[i]] - 1);//多加了一个它自己
    return 0;
}

  

1294. 樱花

题目:

给定一个整数 n,求有多少正整数数对 (x,y) 满足 1/x+1/y=1/n!。

输入格式

一个整数 n。

输出格式

一个整数,表示满足条件的数对数量。

答案对 10^9+7 取模。

数据范围

1≤n≤10^6

输入样例:

2

输出样例:

3

样例解释

共有三个数对 (x,y) 满足条件,分别是 (3,6),(4,4),(6,3)。

分析:

这个题,重点就是对数学公式的推导,需要手推出来,才能看出来性质。

推导省略了,详看AcWing

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005, mod = 1e9 + 7;
int n;
int primes[N], cnt;
bool st[N];
void get_primes(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i]) primes[cnt ++ ] = i;
        for (int j = 0; primes[j] * i <= n; j ++ )
        {
            st[i * primes[j]] = 1;
            if (i % primes[j] == 0) break;
        }
    }
}
int main()
{
    cin >> n;
    get_primes(n);
    long long ans = 1;
    for (int i = 0; i < cnt; i ++ )
    {
        int p = primes[i];
        int s = 0;
        for (int j = n; j >= 1; j /= p) s += j / p;
        ans = ans * (2 * s + 1) % mod;
    }
    cout << ans << endl;
    return 0;
}

  

198. 反素数

题目:

对于任何正整数 x,其约数的个数记作 g((x),例如 g(1)=1、g(6)=4。

如果某个正整数 x 满足:对于任意的小于 x 的正整数 i,都有 g(x)>g(i),则称 为反素数。

例如,整数1,2,4,6 等都是反素数。

现在给定一个数 N,请求出不超过 N 的最大的反素数。

输入格式

一个正整数 N。

输出格式

一个整数,表示不超过 N 的最大反素数。

数据范围

1≤N≤2∗109

输入样例:

1000

输出样例:

840

分析:

这里的代码还是要看以前的提交的,DFS还是不行,这时硬伤

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
ll mx, num;
ll n;
void dfs(ll u, ll last, ll val, ll cnt)
{
    // printf("u = %lld, val = %lld, cnt = %lld\n", u, val, cnt);
        if (cnt > num || (cnt == num && val < mx))
        {
            mx = val;
            num = cnt;
        }
    if (u == 9)
        return;
    for (int i = 1; i <= last; i ++ )
    {
        if (val * a[u] > n) break;
        val *= a[u];
        dfs(u + 1, i, val, cnt * (i + 1));
    }
}
int main(){
    cin >> n;
    dfs(0, 30, 1, 1);
    cout << mx << endl;
    return 0;
}

  

200. Hankson的趣味题

题目:

Hanks 博士是 BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫 Hankson。

现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。

今天在课堂上,老师讲解了如何求两个正整数 c1 和 c2 的最大公约数和最小公倍数。

现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:

已知正整数 a0,a1,b0,b1,设某未知正整数 xx 满足:

  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 的个数;

数据范围

1≤n≤2000,
1≤a0,a1,b0,b1≤2∗109

输入样例:

2
41 1 96 288
95 1 37 1776

输出样例:

6
2

分析:

将b1所有约数求出来,然后枚举判断是否合法,同时DFS还是不行,这里的的FS需要看原来的代码才写出来的

代码:

#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 100006;
int primes[N], cnt;
bool st[N];
ll a0, a1, b0, b1;
PII divi[100];
int tot;
int dv[N], ct;
void get_primes()
{
    for (int i = 2; i <= N; i ++ )
    {
        if (!st[i]) primes[cnt ++ ] = i;
        for (int j = 0; primes[j] * i <= N; j ++ )
        {
            st[i * primes[j]] = 1;
            if (i % primes[j] == 0) break;
        }
    }
}
void dfs(int u, int p)
{
    if (u > tot)
    {
        dv[++ ct] = p;
        return;
    }
    for (int i = 0; i <= divi[u].y; i ++ )
    {
        dfs(u + 1, p);
        p *= divi[u].x;
    }
}
int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a % b);
}
int lcm(int a, int b)
{
    return 1ll * a * b / gcd(a, b);
}
void work()
{
    cin >> a0 >> a1 >> b0 >> b1;
    ll t = b1;
    tot = 0;
    for (int i = 0; primes[i] <= t / primes[i]; i ++ )
    {
        int p = primes[i], s = 0;
        if (t % p == 0)
        {
            while (t % p == 0 ) s ++, t /= p;
            divi[++ tot] = {p, s};
        }
    }
    if (t > 1) divi[++ tot] = {t, 1};
    
    ct = 0;
    dfs(1, 1);
    
    int res = 0;
    for (int i = 1; i <= ct; i ++ )
    {
        int x = dv[i];
        if (gcd(x, a0) == a1 && lcm(x, b0) == b1) res ++;
    }
    printf("%d\n", res);
}
int main()
{
    int T; cin >> T;
    get_primes();
    while (T -- )
    {
        work();
    }
    return 0;
}

  

 

posted @ 2021-07-22 22:36  rookie161  阅读(318)  评论(0编辑  收藏  举报