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;
}
代码思路
一、整体思路
-
输入部分:通过
cin>>n>>k;
从标准输入读取两个正整数n
和k
,分别代表题目中的给定数值和序列长度的上限约束条件。 -
输出部分:首先输出
2
,表示构造的正整数序列a
的长度为2
。接着输出n - 1
和1
,这两个数构成了满足题目条件的序列a
的两个元素。
二、原理分析
-
输出序列长度为 2 的合理性:题目中给定序列长度
m
的范围是2≤m≤k
,这里选择输出长度为 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;
}
代码思路
一、整体思路
- 矩阵快速幂部分:
- 通过预处理一系列矩阵
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)
。
- 通过预处理一系列矩阵
- 处理输入部分:
- 读取输入的询问次数
t
。然后对于每次询问,读取整数n
和d
。接着重新计算m = 1 << d
。 - 初始化数组
ff
,并将ff[0]
设为 1。然后通过位运算判断n
的二进制表示中特定位置是否为 1,如果是,则进行一系列矩阵运算来更新ff
数组。 - 最后,通过遍历
ff
数组计算答案ans
,并输出。
- 读取输入的询问次数
二、原理详解
-
矩阵快速幂:
- 矩阵乘法部分:对于两个矩阵
m1
和m2
,以及矩阵大小size
,通过三层循环实现矩阵乘法。外层的两个循环遍历结果矩阵的行和列,最内层的循环遍历中间的矩阵索引k
,根据矩阵乘法的定义计算结果矩阵的每个元素。每次计算结果时都对p = 998244353
取模,以保证结果在给定的范围内。 - 快速幂部分:通过不断地将矩阵自乘,实现了类似于整数快速幂的效果。这样可以在对数时间内计算出矩阵的高次幂,提高了计算效率。
- 矩阵乘法部分:对于两个矩阵
-
处理输入与更新
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
。
- 读取输入的
-
计算答案
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;
}
代码思路
一、整体思路
- 对于输入的正整数
N
,首先判断如果N
小于 200,则通过三重循环暴力枚举的方式计算满足特定条件的情况数量,然后输出结果。 - 如果
N
不小于 200,则使用筛法(这里具体是线性筛)预处理出小于等于sqrt(N)
的所有质数,并计算莫比乌斯函数mu
和欧拉函数phi
。 - 接着通过一些复杂的数学推导和计算,最终得到结果并输出。
二、具体步骤原理
-
筛法预处理(函数
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)
。
- 首先,初始化三个容器:
-
主函数处理(
main
函数)- 首先,读取输入的正整数
N
。如果N
小于 200,则通过暴力枚举的方式求解。具体来说,对于每个n
从 1 到N
,再对每个j
从 1 到n
,计算x = n + j
和y = n * j
,如果y % x == 0
(即y
能被x
整除)且三个数n
、j
和y / x
的最大公约数为 1,则计数器ans
加一。最后输出ans * 2 - 1
。 - 如果
N
不小于 200:计算sqrtN
(N
的平方根取整),并在必要时进行调整,使得(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
。
- 首先,读取输入的正整数
三、数学原理
- 莫比乌斯函数
mu
和欧拉函数phi
在数论中有重要的性质和应用。莫比乌斯函数在一些数论问题中用于计数和判别性质,欧拉函数则与互质的数的个数有关。 - 代码中的各种计算和推导可能基于特定的数论公式和性质,通过巧妙地利用这些函数和数学关系,来高效地求解问题。具体的数学推导过程可能比较复杂,需要深入的数论知识才能完全理解。