同模余定理

公式

(a+b)%c=(a%c+b%c)%c;
(a-b)%c=(a%c-b%c)%c;
(ab)%c=(a%cb%c)%c;

大数取模

另一类考点就是对大数进行取模
对于大数的求余,联想到进制转换时的方法,得到
举例如下,设大数m=1234,模n
就等于
((((1×10)%n+2%n)%n×10%n+3%n)%n*10%n+4%n)%n

最大公约数

// 先除后乘避免溢出,__gcd参数需为同类型(均为long long) a / __gcd(a, b) * b;

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
#include <bits/stdc++.h>

using namespace std;

long long lcm_n(vector<int> &a)
{
long long result=a[0];
for(int i=1;i<a.size();i++){
//__gcd 要求两个参数类型完全一致
long long num=a[i];
result=a[i]/__gcd(result,num)*result;
}
return result;
}

int main ()
{
int n;
while(cin>>n){
vector <int> a(n);
for(int i=0;i<n;i++){
cin>>a[i];
}
cout<<lcm_n(a)<<endl;
}
}

斐波那契数列

1479

注意取模时机,注意开动态数组的大小,注意循环的条件,注意输出的下标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <bits/stdc++.h>

using namespace std;


int main ()
{
int n;
while(cin>>n){
//开数组要开n+1
vector<int> a(n+1);
a[0]=1;
a[1]=1;
a[2]=2;
for(int i=3;i<=n;i++){
//虽然题目说是对答案取模,但我们需要在每一步取模防溢出
a[i]=(a[i-1]+a[i-2])%2333333;
}
cout<<a[n]<<endl;
}

}

1111

当n较小时写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <bits/stdc++.h>

using namespace std;


int main ()
{
long long a[72]={0,1,1};
for(int i=3;i<72;i++){
a[i]=a[i-1]+a[i-2]+a[i-3];
}
int n;
while(cin>>n){
cout<<a[n+1]<<endl;
}
}

2013

斐波那契数列递归写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits/stdc++.h>

using namespace std;

int f(int n){
if(n==0) return 1;
else if(n==1) return 1;
else return f(n-1)+f(n-2);
}
int main ()
{
int n;
cin>>n;
cout<<f(n);
}

1724

双变量迭代解法

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
#include <bits/stdc++.h>

using namespace std;
//大数取模这样写
const int MOD = 1000000007;
int main ()
{
int n;
cin >> n;
if(n == 0){
cout << 0 % MOD;
return 0;
}
if((n == 1)||(n == 2)){
cout << 1 % MOD;
return 0;
}
long long a=1;
long long b=1;
long long res=0;
for(int i=3;i<=n;i++){
res=(a+b)%MOD;
a=b;
b=res;
}
cout<<res<<endl;
}

素数

1013

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
#include <bits/stdc++.h>

using namespace std;

int main ()
{
int n;
cin >> n;
//一定记得对n是否为1进行判断
if(n == 1){
cout << 2 << endl;
return 0;
}
for(int i=n; ; i++){
int flag = 0;
for(int j = 2; j < sqrt(i); j++){
if(i % j == 0){
//flag=1不是素数
flag = 1;
break;
}
}
//flag=0说明找到素数了
if(flag == 0){
cout << i << endl;
break;
}
}
return 0;
}

1355

素数判定,需要用一个flag

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
#include <bits/stdc++.h>

using namespace std;

int main ()
{
int n;
while(cin>>n){
//一开始假设这个数是素数
int flag=0;
if(n==1){
cout<<"no"<<endl;
continue;
}
if(n==2){
cout<<"yes"<<endl;
continue;
}
//说白了这个程序是对素数判否的,flag=1说明不是素数
for(int i=2;i<=sqrt(n);i++){
if(n%i==0){
flag=1;
break;
}
}
if(flag){
cout<<"yes"<<endl;
}
else
cout<<"no"<<endl;
}
}

素数筛选

1375

空格,无素数判断。全部使用标志位

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
42
43
44
45
#include <bits/stdc++.h>

using namespace std;

int main ()
{
int n;
while(cin>>n){
int meiyou=0;
int kg=1;
int flag=0;
if(n==2){
cout<<-1<<endl;
continue;
}
for(int i=2;i<n;i++){
flag=0;
for(int j=2;j<=sqrt(i);j++){
if(i%j==0){
flag=1;
break;
}

}
if(flag==0&&kg==1){
if(i%10==1){
cout<<i;
kg=0;
meiyou=1;
}
}
else if(flag==0&&kg==0){
if(i%10==1){
cout<<' '<<i;
meiyou=1;
}
}
}
if(!meiyou){
cout<<-1;
}
cout<<endl;
}
}

分解质因数

1156

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
#include <bits/stdc++.h>

using namespace std;

int count_num (int a){
int num=0;
while(a%2==0){
num++;
a/=2;
}
for(int i=3;i<=sqrt(a);i+=2){
while(a%i==0){
num++;
a/=i;
}
}
// 如果最后剩余的数大于1,说明它本身是一个质因数
if (a > 1) {
num++;
}
return num;
}


int main ()
{
int n;
while(cin>>n){
cout<<count_num(n)<<endl;
}
}

1284(重点)

思路

第一步:把 a 拆成 “最小积木块”(质因数分解)

a 就像一个 “成品积木”,我们要先把它拆成不能再拆的 “基础积木”(质因数)。

  • 比如 a=10,拆成 2×5(2 和 5 都是质数,没法再拆);
  • 比如 a=8,拆成 2×2×2(3 个 2);
  • 比如 a=12,拆成 2×2×3(2 个 2 + 1 个 3)。

这些 “基础积木” 的数量是固定的:拼 1 个 10 需要「1 个 2 + 1 个 5」,拼 1 个 8 需要「3 个 2」。

第二步:统计 n! 里有多少块 “基础积木”

n! 是 “从 1 到 n 所有数的乘积”,我们可以把这些数也都拆成 “基础积木”,然后统计每种积木的总数量。

  • 比如 6! = 6×5×4×3×2×1,拆成积木:

    6=2×3,5=5,4=2×2,3=3,2=2,1 = 无 → 总共有:4 个 2、2 个 3、1 个 5。

  • 统计技巧(不用全拆):比如统计 6! 里 2 的数量,直接算「6÷2=3(能被 2 整除的数有 3 个:2、4、6) + 6÷4=1(能被 4 整除的数有 1 个:4,额外多 1 个 2) + 6÷8=0」→ 总共 4 个 2,和拆积木结果一致。

第三步:算 “最多能拼多少个 a”(求最大 k)

拼 1 个 a 需要固定数量的基础积木,我们用 n! 里的积木总数 ÷ 拼 1 个 a 需要的积木数,取最小的那个结果,就是最大的 k。

  • 例子 1(输入 6 10):

    拼 1 个 10 需要「1 个 2 + 1 个 5」;

    n! 里有 4 个 2、1 个 5;

    2 的数量够拼:4 ÷ 1 = 4 个 10;

    5 的数量够拼:1 ÷ 1 = 1 个 10;

    因为 5 的数量只够拼 1 个,所以最多只能拼 1 个 10 → k=1。

  • 例子 2(输入 10 12):

    a=12 拆成「2 个 2 + 1 个 3」;

    10! 里有 8 个 2、4 个 3;

    2 的数量够拼:8 ÷ 2 = 4 个 12;

    3 的数量够拼:4 ÷ 1 = 4 个 12;

    两者都够拼 4 个,所以 k=4。

  • 例子 3(输入 5 8):

    a=8 拆成「3 个 2」;

    5! 里有 3 个 2;

    2 的数量够拼:3 ÷ 3 = 1 个 8 → k=1。

核心逻辑总结(一句话)

要让 n! 整除 a^k,本质是 n! 里的 “基础积木(质因数)” 数量,能支撑拼出 k 个 a;而最大的 k,由 “最缺的那块积木” 决定(即各质因数可拼数量的最小值)。

再举个生活化的例子

比如:

  • n! 相当于你手里有:4 个鸡蛋、1 个番茄(对应 6! 里的 4 个 2、1 个 5);
  • a 相当于 “番茄炒蛋” 这道菜,做 1 份需要:1 个鸡蛋 + 1 个番茄(对应 10 需要 1 个 2+1 个 5);
  • 你最多能做几份?鸡蛋够做 4 份,但番茄只够做 1 份 → 最多 1 份(k=1)。

注意点

首先,n阶乘超过20就不能正常计算

其次,数组不能被传递,真需要用到数组,使用void,然后传参传进来

然后,找木桶中的最短木板类问题时,int max_k = INT_MAX;max_k = min(max_k, arr_n[i] / arr_a[i]);俗称高开低走

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
42
43
#include <bits/stdc++.h>

using namespace std;

//数组不能被传递,真需要用到数组,使用void,然后传参传进来
void f(int a,int arr[]){
while(a%2==0){
arr[2]++;
a/=2;
}
for(int i=3;i * i <= a;i+=2){
while(a%i==0){
arr[i]++;
a/=i;
}
}
if(a>1){
arr[a]++;
}
}


int main ()
{
int n,a;
long long num = 1; // 改用long long减少小范围溢出(按你要求仅临时优化)
cin>>n>>a;
int arr_n[1001] = {0}, arr_a[1001] = {0};
for(int i=2;i<=n;i++){
f(i,arr_n);
}
f(a,arr_a);
// 修正:k值计算逻辑(核心:找最大的k,使k×arr_a[i] ≤ arr_n[i])
int max_k = INT_MAX;
// 遍历a的所有质因数(仅关注arr_a[i]>0的位置)
for(int i = 2; i <= 49; i++) {
if(arr_a[i] == 0) continue; // a中无此质因数,跳过
// 核心逻辑:n!中i的个数 ÷ a中i的个数 = 该质因数能支撑的最大k
max_k = min(max_k, arr_n[i] / arr_a[i]);
}
cout << max_k << endl;
}

1464(重点)

注意点:

如何获取最大素因数?

获取的字符串怎么转化成数字?

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <bits/stdc++.h>

using namespace std;
//获取最大素因数
int f(int a){
int arr[1000]={0};
int j=0;
int jsq=0;
while(a%2==0){
a/=2;
arr[j++]=2;
jsq++;
}
for(int i=3;i<=sqrt(a);i++){
if(a%i==0){
a/=i;
arr[j++]=i;
jsq++;
}
}
if(a>1){
arr[j++]=a;
jsq++;
}
sort(arr,arr+jsq);
return arr[jsq-1];
}


int main ()
{
int n;
while(cin>>n){
cin.ignore(); // 忽略换行符,避免getline读取空行
//没必要开字符串数组
string s;
for(int i=0;i<n;i++){
getline(cin, s); // 读取整行(含空格/特殊字符)
string num_str;
for (char c : s) {
if (c >= '0' && c <= '9') {
num_str += c; // 正确拼接数字字符串
}
}
int num = 0;
if (!num_str.empty()) {
num = stoi(num_str); // 转为整数
}
/* int len=0;
* string num;
* int k=0;
* int z=0;
* for(char c:s[k++]){
* if(c>='0'&&c<='9'){
* num[z++]+=c;
* len++;
* }
* }
* if(len==0){
* cout<<0<<endl;
* continue;
* }
*/

if (num == 0) {
cout << 0 << endl;
continue;
}

cout<<f(num)<<endl;
}
}
}

二分快速求幂

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
#include <bits/stdc++.h>

using namespace std;


long long f(long long x,long long n,long long mod){
long long result=1;
while(n>0){
//每次拿最后一位,若为1则乘上权重
if(n&1) result=result*x%mod;
//更新权重
x=x*x%mod;
//指数的二进制右移一位
n>>=1;
}
return result;
}

int main ()
{
long long x,n;
cin>>x>>n;
cout<<f(x,n,233333);
}

数学公式

判断共线

不能使用除法会有精度损失,叉乘可以表示围城的平行四边形的面积,即两个向量 “同向程度” 的打分

  1. 叉乘 vs 点乘(最关键区别)

✅ 叉乘

公式:

a*d - b*c

作用:

判断两个向量是否共线(是否在一条直线上)

=0 → 共线

≠0 → 不共线

✅ 点乘

公式:

a*c + b*d

作用:

判断两个向量是否垂直!

=0 → 垂直

≠0 → 不垂直

1679

一、核心数学思路

  1. 建立位置与时间的关系

设时间为t (t≥0),则:

  • 小王的位置:(x1 + u1*t, y1 + v1*t)
  • 小明的位置:(x2 + u2*t, y2 + v2*t)
  1. 距离的平方(避免开根号,简化计算)

两点距离的平方公式:

d2(t)=[(x1+u1t)−(x2+u2t)]2+[(y1+v1t)−(y2+v2t)]2

展开整理为二次函数形式 d²(t) = at² + bt + c

  • dx = x1 - x2(初始 x 差),du = u1 - u2(x 方向速度差)
  • dy = y1 - y2(初始 y 差),dv = v1 - v2(y 方向速度差)
  • 则:a=du2+dv2b=2∗(dx∗du+dy∗dv)c=dx2+dy2
  1. 求最短距离(分 3 种情况)

二次函数 at² + bt + c 的最小值取决于系数a

  • 情况 1:a = 0(速度差为 0,两人相对静止)→ 最短距离就是初始距离;

  • 情况 2:a > 0

    (二次函数开口向上)→ 最小值在

    1
    t = -b/(2a)

    处:

    • t ≥ 0 → 最短距离为 √(a*t² + bt + c)
    • t < 0 → 最短距离为初始距离(t=0 时);
  • 情况 3:a < 0(理论不存在,因为 a 是平方和,必≥0)。