YBTOJ 6.2质数与约数

A.线性筛素数

image
image

先记下埃式筛

for (int i = 2; i <= n; ++i) {
	if (vis[i]) continue;
	p[++top] = i;
	for (int j = 2; i * j <= n; ++i) vis[i * j] = 1;
}

看起来就很容易理解
时间复杂度 \(\text O(n \log \log n)\) 我不会证 知道就行了

不是线性的 过不掉这题
但是如果要预处理出 \(\left[1, n\right]\) 中所有数的质因数 还是要用埃式筛的

然后是欧拉筛 也称线性筛
用每个数的最小质因子把它筛掉
先上代码

for (int i = 2; i <= n; ++i) {
	if (!vis[i])
	p[++top] = i;
	for (int j = 1; p[j] * i <= n; ++j) {
		vis[i * p[j]] = 1;
		if (i % p[j] == 0)
		break;
	}
}
  • 为什么是这么写

考虑枚举 \(j\) 的那个循环

如果循环还没有跳出 说明前面的每一个质数都不是 \(i\) 的因数 那么在 \(i \times p_j\)\(p_j\) 一定是最小质因子

  • 为什么如果 \(p_j\)\(i\) 的因数时先筛掉一个再跳出循环

这个我推荐你手搓(

假设存在一个数的的最小质因子为 \(x\)
且该数可以拆成 \(x \times x \times y\) 的形式

那么我们 \(j\) 循环枚举质数的时候 它一定是被 \(p_j = x\) 时的 \(p_j \times (x \times y)\) 筛掉
其中 \(x \times y\) 就是那个 \(i\)

完整代码还用贴吗(


B.质数距离

image
image

要用到一个结论:对于一个合数 \(x\) 它的最小质因子一定 \(\le \sqrt x\)

挺显然的 考虑反证

\(x\) 的最小质因子为 \(p\)\(p > \sqrt x\)
\(\frac{x}{p} < \sqrt x\)
说明 \(x\) 一定有比 \(p\) 小的因子
那么 \(p\) 就不是最小质因子了 与假设矛盾

那么对于此题 我们可以筛出 \(\left[1, \sqrt r \right]\) 范围内的质数
然后暴力枚举 \(\left[l, r\right]\) 的每一个数 暴力枚举筛除来的质数能不能整除它
然后暴力扫一遍统计答案就行了

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int N = 1e6 + 0721;
const int M = 1e7 + 0721;
int p[N], cnt;
bool vis[M];
ll ans[M];
ll l, r;

void init(ll n) {
    cnt = 0;
    for (ll i = 2; i <= n; ++i) {
        if (!vis[i])
            p[++cnt] = i;
        for (ll j = 1; j <= cnt && p[j] * i <= n; ++j) {
            vis[p[j] * i] = 1;
            if (i % p[j] == 0)
                break;
        }
    }
}

int main() {
    while (scanf("%lld%lld", &l, &r) != EOF) {
        memset(vis, 0, sizeof vis);
        if (l == 1) {
            init(r);
            if (cnt < 2) {
                printf("There are no adjacent primes.\n");
                continue;
            }
            int min1 = p[1], min2 = p[2];
            int max1 = p[1], max2 = p[2];
            for (int i = 2; i < cnt; ++i) {
                if (p[i + 1] - p[i] < min2 - min1) {
                    min2 = p[i + 1];
                    min1 = p[i];
                }
                if (p[i + 1] - p[i] > max2 - max1) {
                    max2 = p[i + 1];
                    max1 = p[i];
                }
            }
            printf("%d,%d are closest, %d,%d are most distant.\n", min1, min2, max1, max2);
        } else {
            //			cout << (ll)(sqrt(r) + 1) << '\n';
            init(50000);
            memset(vis, 0, sizeof vis);
            for (int i = 1; i <= cnt; ++i) {
                ll j = l / p[i];
                if (j * p[i] < l)
                    ++j;
                if (j == 1)
                    ++j;
                //				ll x = (l + p[i] - 1) / p[i];
                //				ll j = max(2, (int)x);
                for (; j * p[i] <= r; ++j) {
                    ll x = j * p[i] - l;
                    vis[x] = 1;
                }
            }
            cnt = 0;
            for (ll i = 0; i <= r - l; ++i) {
                if (!vis[i])
                    ans[++cnt] = i + l;
            }
            //			for (int i = 1; i <= cnt; ++i) cout << ans[i] << ' ';
            if (cnt < 2) {
                printf("There are no adjacent primes.\n");
                continue;
            }
            ll min1 = ans[1], min2 = ans[2];
            ll max1 = ans[1], max2 = ans[2];
            for (int i = 2; i < cnt; ++i) {
                if (ans[i + 1] - ans[i] < min2 - min1) {
                    min2 = ans[i + 1];
                    min1 = ans[i];
                }
                if (ans[i + 1] - ans[i] > max2 - max1) {
                    max2 = ans[i + 1];
                    max1 = ans[i];
                }
            }
            printf("%lld,%lld are closest, %lld,%lld are most distant.\n", min1, min2, max1, max2);
        }
    }

    return 0;
}

C.不定方程

image
image

非常吃数学功底的一道题

首先我们要树立一个正确的思路:要求正整数解 \((x, y)\) 的对数 我们考虑用 \(x\) 来表示 \(y\)

\(\frac 1 x + \frac 1 y = \frac 1 {n!}\)

\(\frac{x + y}{xy} = \frac{1}{n!}\)

$ x {n!} + y {n!} = xy$

\(x {n!} = y(x - n!)\)

\(y = \frac{x n!}{x - n!}\)

然后不会了

考虑换元 设 \(t = x - n!\)\(x = t + n!\)

\(y = \frac{x n!}{x - n!} = \frac{(t + n!)n!}{t} = n! + \frac{{n!}^2}{t}\)

现在就很明了了 实际我们只需要求 \({n!}^2\) 的因数个数即可

考虑算数基本定理:

\(x = \prod\limits_{i = 1}^{n} {p_i}^{c_i}\)\(x\) 的因数个数为 \(\prod\limits_{i = 1}^{n} (c_i + 1)\)

很好理解 对于每个质因子的集合加上选 \(0\) 个一共就有 \(c_i + 1\) 种选择 乘起来就是上面那玩意

考虑 \(n!\) 的质因数分解 它是所有比它小的数的乘积
那么对于一个质因子的出现次数 一定是 \(\sum\limits_{i = 1}^n i的该因子数目\)

再考虑平方

\(x = \prod\limits_{i = 1}^n {p_i}^{c_i}\)
那么 \(x ^ 2 = \prod\limits_{i = 1}^n {p_i} ^ {2c_i}\)

然后终于就做完了

点击查看代码
#include <bits/stdc++.h>
#define ll long long

#define int long long

using namespace std;

const int N = 1e5 + 0721;
const int M = 1e8 + 0721;
const int mod = 1e9 + 7;
int p[N], cnt;
int c[N];
bool vis[M];
int n;

void primes() {
    for (int i = 2; i <= n; ++i) {
        if (!vis[i])
            p[++cnt] = i;
        for (int j = 1; j <= cnt && p[j] * i <= n; ++j) {
            vis[p[j] * i] = 1;
            if (i % p[j] == 0)
                break;
        }
    }
}

signed main() {
    scanf("%d", &n);
    primes();
    for (int i = 1; i <= cnt; ++i) {
        for (int j = p[i]; j <= n; j *= p[i]) {
            c[i] += n / j;
            c[i] %= mod;
        }
    }
    ll ans = 1;
    for (int i = 1; i <= cnt; ++i) ans = ans * (c[i] * 2 + 1) % mod;
    printf("%lld", ans);

    return 0;
}

D.反素数

image
image

考虑算数基本定理

假设我们固定一个拆分 \({c_1, c_2, ..., c_n}\)
那么满足此条件的最小数显然是 \(2^{c_1} \times 3^{c_2} \times ... \times p_n^{c_n}\)
质数为连续的时候最小

发现 \(2 \times 3 \times 5 \times 7 \times 11 \times 13 \times 17 \times 19 \times 23 \times 29 \times 31 > 2 \times 10^9\)
所以只需要 dfs 枚举这些数的幂次即可 再加上一个显而易见的最优性剪枝就能过掉

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;

int prime[12] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
int c[12];
ll n, num, ans;

void dfs(ll now, int p, ll cnt) {
	if (now > n) return;
	if (p > 11) return;
	if (cnt > num || (cnt == num && now < ans)) {
		ans = now;
		num = cnt;
	}
	for (int i = 0; i <= 32; ++i) {
		c[p] = i;
		dfs(now, p + 1, cnt * (i + 1));
		now *= prime[p];
		if (now > n) return;
	}
}

int main() {
	scanf("%d", &n);
//	c[0] = 32;
	dfs(1, 1, 1);
	printf("%lld",ans);
	
	return 0;
}

其实进一步观察可以发现 显然 \({c_1, c_2, ..., c_n}\) 单调不增的时候才最小
所以其实当前枚举到上一个数的幂次即可
然而因为寻址需要时间 这个甚至没有上面那个跑得快((

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int prime[12] = { 0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 };
int c[12];
ll n, num, ans;

void dfs(ll now, int p, ll cnt) {
    if (now > n)
        return;
    if (p > 11)
        return;
    if (cnt > num || (cnt == num && now < ans)) {
        ans = now;
        num = cnt;
    }
    for (int i = 0; i <= c[p - 1]; ++i) {
        c[p] = i;
        dfs(now, p + 1, cnt * (i + 1));
        now *= prime[p];
        if (now > n)
            return;
    }
}

int main() {
    scanf("%d", &n);
    c[0] = 32;
    dfs(1, 1, 1);
    printf("%lld", ans);

    return 0;
}

E.余数之和

image
image

需要用到整除分块

想到了线性复杂度 我是天才!!!

考虑把模数拆掉:

\(x \mod k = x - \left\lfloor\dfrac{x}{k}\right\rfloor \times k\)

那么这题答案就是 \(\sum\limits_{i = 1}^k n - \left\lfloor\dfrac{n}{i}\right\rfloor \times i\)
这个东西整除分块弄一下就完事了

点击查看代码
#include <bits/stdc++.h>
#define ll long long

using namespace std;
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    ll ans = 1ll * n * k;
    ll lst;

    for (int i = 1; i <= n; i = lst + 1) {
        if (k / i != 0)
            lst = min(n, k / (k / i));
        else
            lst = n;
        ans -= (k / i) * (i + lst) * (lst - i + 1) / 2;
    }

    printf("%lld", ans);

    return 0;
}
posted @ 2023-09-06 20:20  Steven24  阅读(24)  评论(0编辑  收藏  举报