牛客练习赛128(上)

Cidoai的幂次序列

题目描述

登录—专业IT笔试面试备考平台_牛客网

运行代码

#include <bits/stdc++.h>
using namespace std;
signed main(){
    long long n,k;
    cin>>n>>k;
    cout<<2<<'\n';
    cout<<n-1<<' '<<1<<'\n';
    return 0;
}

代码思路

一、整体思路

  1. 输入部分:通过 cin>>n>>k; 从标准输入读取两个正整数 n 和 k,分别代表题目中的给定数值和序列长度的上限约束条件。

  2. 输出部分:首先输出 2,表示构造的正整数序列 a 的长度为 2。接着输出 n - 1 和 1,这两个数构成了满足题目条件的序列 a 的两个元素。

二、原理分析

  1. 输出序列长度为 2 的合理性:题目中给定序列长度 m 的范围是 2≤m≤k,这里选择输出长度为 2 是一种较为简单直接的策略,在满足题目条件的同时尽量简化输出。

  2. 选择输出 n - 1 和 1的原因

    • 根据题目要求,需要构造一个正整数序列 a,使得 ∑(i=0 到 m - 1) a[i]^a[(i + 1)mod m] = n
    • 当输出 n - 1 和 1时,计算可得:
      • (n - 1)^1 + 1^(n - 1) = n - 1 + 1 = n,满足题目要求的等式条件。
    • 同时,题目中规定所有 a[i] 都有 0 < a[i] ≤ n,输出的 n - 1 和 1都满足这个条件。

Cidoai的映射数列

题目描述

登录—专业IT笔试面试备考平台_牛客网

运行代码

#include <iostream>
#include <vector>

using namespace std;
typedef long long LL;

const LL p = 998244353;

struct Matrix {
    vector<vector<LL>> mat;
    Matrix(LL size) { mat.resize(size, vector<LL>(size, 0)); }
};

Matrix multiplyMatrices(const Matrix& m1, const Matrix& m2, LL size) {
    Matrix result(size);
    for (LL i = 0; i < size; i++) {
        for (LL j = 0; j < size; j++) {
            for (LL k = 0; k < size; k++) {
                result.mat[i][j] = (result.mat[i][j] + m1.mat[i][k] * m2.mat[k][j]) % p;
            }
        }
    }
    return result;
}

int main() {
    vector<vector<Matrix>> f(8);
    for (LL d = 0; d <= 7; d++) {
        LL m = 1 << d;
        f[d].resize(61, Matrix(m));
        for (LL i = 0; i < m; i++) {
            f[d][0].mat[i][(i * 2 + 1) % m] = 1;
            for (LL j = 0; j < d; j++) {
                if ((i >> j) & 1) {
                    f[d][0].mat[i][(i - (1 << j)) * 2 % m] = 1;
                }
            }
        }
        for (LL i = 1; i <= 60; i++) {
            f[d][i] = multiplyMatrices(f[d][i - 1], f[d][i - 1], m);
        }
    }
    LL t;
    cin >> t;
    while (t--) {
        LL n, d;
        cin >> n >> d;
        LL m = 1 << d;
        vector<LL> ff(m, 0);
        ff[0] = 1;
        for (LL i = 0; i <= 60; i++) {
            if ((n >> i) & 1) {
                vector<LL> g(m, 0);
                for (LL j = 0; j < m; j++) {
                    for (LL k = 0; k < m; k++) {
                        g[k] = (ff[j] * f[d][i].mat[j][k] + g[k]) % p;
                    }
                }
                ff = g;
            }
        }
        LL ans = 0;
        for (LL i = 0; i < m; i++) {
            ans = (ans + ff[i]) % p;
        }
        cout << ans << endl;
    }
    return 0;
}

代码思路

一、整体思路

  1. 矩阵快速幂部分
    • 通过预处理一系列矩阵 f,其中 f[d][i] 表示在特定条件下的矩阵状态。这里的 d 取值从 0 到 7,可能与某种特定的分类或参数有关;i 表示步数或幂次。
    • 对于每个 d,首先初始化 m = 1 << d,即 2 的 d 次方,这个 m 可能代表矩阵的大小。然后初始化 f[d][0] 的矩阵元素,根据特定的规则进行赋值。接着通过矩阵快速幂的方法,不断地将 f[d][i] 更新为 f[d][i - 1] 自乘的结果,即 f[d][i]=multiplyMatrices(f[d][i - 1], f[d][i - 1], m)
  2. 处理输入部分
    • 读取输入的询问次数 t。然后对于每次询问,读取整数 n 和 d。接着重新计算 m = 1 << d
    • 初始化数组 ff,并将 ff[0] 设为 1。然后通过位运算判断 n 的二进制表示中特定位置是否为 1,如果是,则进行一系列矩阵运算来更新 ff 数组。
    • 最后,通过遍历 ff 数组计算答案 ans,并输出。

二、原理详解

  1. 矩阵快速幂

    • 矩阵乘法部分:对于两个矩阵 m1 和 m2,以及矩阵大小 size,通过三层循环实现矩阵乘法。外层的两个循环遍历结果矩阵的行和列,最内层的循环遍历中间的矩阵索引 k,根据矩阵乘法的定义计算结果矩阵的每个元素。每次计算结果时都对 p = 998244353 取模,以保证结果在给定的范围内。
    • 快速幂部分:通过不断地将矩阵自乘,实现了类似于整数快速幂的效果。这样可以在对数时间内计算出矩阵的高次幂,提高了计算效率。
  2. 处理输入与更新 ff 数组

    • 读取输入的 n 和 d 后,根据 d 计算出矩阵大小 m
    • ff 数组的作用可能是存储某种中间状态或结果。初始时将 ff[0] 设为 1,然后根据 n 的二进制表示进行更新。如果 (n >> i) & 1 为真,说明 n 的二进制表示在第 i 位为 1,此时进行一系列矩阵运算来更新 ff 数组。具体来说,先初始化一个临时数组 g,然后通过三层循环计算 g[k],这里的计算过程涉及到之前预处理的矩阵 f[d][i] 和当前的 ff 数组。最后将 ff 更新为 g
  3. 计算答案 ans:遍历更新后的 ff 数组,将所有元素相加并对 p 取模,得到最终的答案 ans。这个答案可能代表了满足特定条件的某种数量或结果。

Cidoai的数论求和

题目描述

登录—专业IT笔试面试备考平台_牛客网

运行代码

#include <iostream>
#include <vector>
#include <cmath>
#include <numeric>
#include <chrono>
#include <cassert>

using namespace std;
using u32 = uint32_t;
using u64 = uint64_t;

vector<bool> is_not_prime;
vector<u32> primes;
vector<int> mu, phi;

void sieve(int N) {
    is_not_prime.resize(N + 1);
    mu.resize(N + 1);
    phi.resize(N + 1);
    mu[1] = 1;
    phi[1] = 1;
    for (int i = 2; i <= N; i++) {
        if (!is_not_prime[i]) {
            primes.push_back(i);
            mu[i] = -1;
            phi[i] = i - 1;
        }
        for (int p : primes) {
            if (i * p > N) break;
            is_not_prime[i * p] = true;
            if (i % p == 0) {
                mu[i * p] = 0;
                phi[i * p] = phi[i] * p;
                break;
            } else {
                mu[i * p] = -mu[i];
                phi[i * p] = phi[i] * (p - 1);
            }
        }
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    u64 N;
    cin >> N;
    if (N < 200) {
        int ans = 0;
        for (int n = 1; n <= N; n++) {
            for (int j = 1; j <= n; j++) {
                int x = n + j;
                int y = n * j;
                if (y % x == 0 && gcd(gcd(n, j), y / x) == 1) {
                    ans++;
                }
            }
        }
        cout << ans * 2 - 1 << '\n';
        return 0;
    }
    int sqrtN = sqrt(N);
    while ((u64)(sqrtN + 1) * (sqrtN + 1) <= N) {
        sqrtN++;
    }
    sieve(sqrtN);
    vector<u64> z(sqrtN + 1);
    for (int i = 2; i <= sqrtN; i++) {
        z[i] = N / i - i;
    }
    u64 ans = 1;
    int minPos = -1;
    for (int i = 2; i <= sqrtN; i++) {
        if (i * 2 > N / i) {
            minPos = i;
            break;
        }
        ans += phi[i];
    }
    for (int i = 1; i <= sqrtN; i++) {
        if (mu[i] == 0) continue;
        int j = (minPos + i - 1) / i * i;
        while (j <= sqrtN) {
            ans += mu[i] * (z[j] / i);
            j += i;
        }
    }
    ans = 2 * ans - 1;
    cout << ans << '\n';
    return 0;
}

代码思路

一、整体思路

  1. 对于输入的正整数 N,首先判断如果 N 小于 200,则通过三重循环暴力枚举的方式计算满足特定条件的情况数量,然后输出结果。
  2. 如果 N 不小于 200,则使用筛法(这里具体是线性筛)预处理出小于等于 sqrt(N) 的所有质数,并计算莫比乌斯函数 mu 和欧拉函数 phi
  3. 接着通过一些复杂的数学推导和计算,最终得到结果并输出。

二、具体步骤原理

  1. 筛法预处理(函数 sieve

    • 首先,初始化三个容器:is_not_prime 用于标记一个数是否为合数,primes 用于存储所有找到的质数,mu 和 phi 分别用于存储莫比乌斯函数值和欧拉函数值。
    • 然后从 2 开始遍历到 N,对于每个数 i:如果 i 不是合数(即 is_not_prime[i] 为 false),则将 i 加入质数列表 primes,并初始化 mu[i] 为 -1(质数的莫比乌斯函数值为 -1),phi[i] 为 i - 1(质数的欧拉函数值为自身减 1)。接着遍历已找到的质数 p,如果 i * p 超过 N,则跳出循环。如果 i 能被 p 整除,那么根据数学性质,此时 mu[i * p] 为 0,phi[i * p] 等于 phi[i] * p;如果 i 不能被 p 整除,那么 mu[i * p] 为 -mu[i]phi[i * p] 等于 phi[i] * (p - 1)
  2. 主函数处理(main 函数)

    • 首先,读取输入的正整数 N。如果 N 小于 200,则通过暴力枚举的方式求解。具体来说,对于每个 n 从 1 到 N,再对每个 j 从 1 到 n,计算 x = n + j 和 y = n * j,如果 y % x == 0(即 y 能被 x 整除)且三个数 nj 和 y / x 的最大公约数为 1,则计数器 ans 加一。最后输出 ans * 2 - 1
    • 如果 N 不小于 200:计算 sqrtNN 的平方根取整),并在必要时进行调整,使得 (sqrtN + 1) * (sqrtN + 1) 严格大于 N。调用 sieve(sqrtN) 预处理出小于等于 sqrtN 的质数以及相关函数值。初始化一个向量 z,对于 i 从 2 到 sqrtN,计算 z[i] = N / i - i。初始化结果 ans 为 1,并找到一个 minPos,使得对于 i 大于等于 minPos,满足 i * 2 > N / i。对于 i 从 2 到 sqrtN,如果 mu[i] 不为 0,则进行特定的计算。具体是通过一个循环找到合适的 j,并更新 ans 的值。最后输出 2 * ans - 1

三、数学原理

  1. 莫比乌斯函数 mu 和欧拉函数 phi 在数论中有重要的性质和应用。莫比乌斯函数在一些数论问题中用于计数和判别性质,欧拉函数则与互质的数的个数有关。
  2. 代码中的各种计算和推导可能基于特定的数论公式和性质,通过巧妙地利用这些函数和数学关系,来高效地求解问题。具体的数学推导过程可能比较复杂,需要深入的数论知识才能完全理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱姌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值