QBXT五一数学营

zhx好闪,拜谢zhx
本人LaTeX存在问题,下课修改(

Day 1

上午

电脑1s跑3e8()

mod

a / b = c . . . d , a = b ⋅ c + d a / b = c...d , a = b \cdot c + d a/b=c...d,a=bc+d

b > d ≥ 0 b > d \ge 0 b>d0

c = a / b , d = a   m o d   b c = a / b , d = a \bmod b c=a/b,d=amodb

( a + b )   m o d   c = ( a   m o d   c + b m o d    c )   m o d   c (a + b) \bmod c = (a \bmod c + b \mod c) \bmod c (a+b)modc=(amodc+bmodc)modc

( a − b )   m o d   c = ( a   m o d   c − b   m o d   c )   m o d   c (a - b) \bmod c = (a \bmod c - b \bmod c) \bmod c (ab)modc=(amodcbmodc)modc

( a ⋅ b )   m o d   c = ( a   m o d   c ) ⋅ ( b   m o d   c )   m o d   c (a \cdot b) \bmod c = (a \bmod c) \cdot (b \bmod c) \bmod c (ab)modc=(amodc)(bmodc)modc

例1
读入n, p,输出n! % p
2 <= p <= 1e9, 1 <= n <= 1000;

ll n = read(), p = read();
ll ans = 1;
if(n >= p){
	cout << 0;
	return 0;
}
for(int i = 2; i <= n; ++i)ans = (ans * i) % p;
cout << ans % p;

gcd & lcm

g = gcd ⁡ ( a , b ) , a = a ′ g , b = b ′ g g = \gcd(a, b), a = a'g, b = b'g g=gcd(a,b),a=ag,b=bg

∴ gcd ⁡ ( a ′ , b ′ ) = 1 \therefore \gcd(a', b') = 1 gcd(a,b)=1

∴ gcd ⁡ ( a − b , b ) = gcd ⁡ ( ( a ′ − b ′ ) g , b ′ g ) = gcd ⁡ ( a ′ − b ′ , b ′ ) ⋅ g = 1 ⋅ g = g \therefore \gcd(a - b, b) = \gcd((a' - b')g, b'g) = \gcd(a' - b', b') \cdot g = 1 \cdot g = g gcd(ab,b)=gcd((ab)g,bg)=gcd(ab,b)g=1g=g

gcd ⁡ ( a , b ) = gcd ⁡ ( a − b , b ) = gcd ⁡ ( a − 2 b , b ) = . . . . . = gcd ⁡ ( a   m o d   b , b ) \gcd(a, b) = \gcd(a - b, b) = \gcd(a - 2b, b) = ..... = \gcd(a \bmod b, b) gcd(a,b)=gcd(ab,b)=gcd(a2b,b)=.....=gcd(amodb,b)

代码
ll gcd(ll a, ll b){return a ? gcd(b % a, a) : b;}

时间复杂度 O ( l o g n ) O(log n) O(logn)

证明:
$a \ge b, \gcd(a, b) = \gcd(b, a \bmod $ b ) b) b)

a   m o d   b < a / 2 a \bmod b < a / 2 amodb<a/2

证明:

分类讨论

b <= a / 2时, a % b < b <= a / 2;

a >= b > a / 2时,a % b = a - b < a / 2;

所以每次 g c d gcd gcd 相当于将 a a a 除以 2 2 2

所以 O ( log ⁡ n ) O(\log n) O(logn)

例1
求gcd(a1, a2, ..., an)

int n = read(), ans;
for(int i = 1; i <= n; ++i)a[i] = read();
ans = a[1];
for(int i = 2; i <= n; ++i)ans = gcd(ans, a[i]);
cout << ans << "\n";

时间复杂度:

a n s ans ans 每次都在变小, 而每次 g c d gcd gcd 都可看做除 2 2 2
所以最多除 $ \log{ans}$ 次

a n s = a 1 ans = a1 ans=a1 , 所以 O ( n + log ⁡ a 1 ) O(n + \log a_1) O(n+loga1)

g = gcd ⁡ ( a , b ) , l = l c m ( a , b ) g = \gcd(a, b), l = lcm(a, b) g=gcd(a,b),l=lcm(a,b)

$ g \cdot l = a \cdot b$

代码

ll lcm(int a, int b){return a / gcd(a, b) * b;}

快速幂

x , y , p ≤ 1 0 9 x, y, p \le 10 ^ 9 x,y,p109

例: y = 37 y = 37 y=37

x 37 = ( x 18 ) 2 ⋅ x x ^ {37} = (x ^ {18}) ^ 2 \cdot x x37=(x18)2x

x 18 = ( x 9 ) 2 x ^ {18} = (x ^ 9) ^ 2 x18=(x9)2

x 9 = ( x 4 ) 2 ⋅ x x ^ 9 = (x ^ 4) ^ 2 \cdot x x9=(x4)2x

x 4 = ( x 2 ) 2 x ^ 4 = (x ^ 2) ^ 2 x4=(x2)2

x 2 = x ⋅ x x ^ 2 = x \cdot x x2=xx

时间复杂度 O ( log ⁡ y ) O(\log y) O(logy)

代码
通用版(循环)
ll qpow(ll a, ll b, ll p){
	if(y == 0)return 1;
	ll res = 1;
	while(b){
		if(b & 1)res = res * a % p;
		b >>= 1;
		a = a * a % p;
	}
	return res;
}
zhx版(递归)
ll ksm(ll x, ll y, ll p){
	if(y == 0)return 1;
	ll z = ksm(x, y >> 2, p);
	return z * z * (y & 1 ? x : 1) % p
}
例1
x, y, p <= 10 ^ 18
求x * y % p
直接求x * y达到10 ^ 36存不下
例 y = 37 用快速幂思想
x * y = x * 37 = x + x + .. = x
x * 37 = x * 18 + x * 18 + x
x * 18 = x * 9 + x * 9
x * 9 = x * 4 + x * 4 + x
x * 4 = x * 2 + x * 2
x * 2 = x + x

代码
ll ksc(ll x, ll y, ll p){
   
	if(y == 0)return 0;
	if(y == 1)return x;
	ll z = ksc(x, y >> 2, p);
	return (z + z + (y & 1 ? x : 0)) % p
}

矩阵

大抵可以看做一个二维数组
被迫使用LaTeX(
矩阵加法与减法

$$
\begin{vmatrix}{}
1 & 2 & 3 \
3 & 2 & 1
\end{vmatrix}

\begin{vmatrix}{}
1 & 2 & 3 \
3 & 2 & 1
\end{vmatrix}

=
\begin{vmatrix}{}
4 & 4 & 4 \
4 & 4 & 4
\end{vmatrix}
$$

矩阵减法同理

矩阵乘法

A ( n ⋅ m ) ⋅ B ( m ⋅ k ) A(n \cdot m) \cdot B(m \cdot k) A(nm)B(mk)
只有第一个矩阵的列数等于第二个矩阵的行数才行

$$
\begin{vmatrix}{}
1 & 2 & 3 \
3 & 2 & 1
\end{vmatrix}

\times

\begin{vmatrix}{}
1 & 2 \
1 & 2 \
1 & 2
\end{vmatrix}

=

\begin{vmatrix}{}
1 * 1 + 2 * 1 + 3 * 1 & 1 * 2 + 2 * 2 + 3 * 2 \
3 * 1 + 2 * 1 + 1 * 1 & 3 * 2 + 2 * 2 + 1 * 2
\end{vmatrix}

=

\begin{vmatrix}{}
6 & 12 \
6 & 12
\end{vmatrix}
$$

代码:

struct Matrix{
   
    ll a[105][105];//自行开空间 
    int n, m;
    Matrix() {
   memset(a, 0, sizeof a);}
    Matrix operator * (const Matrix &A, const Matrix &B){
   //& 直接调用A, B.const 防止 A, B 被修改 
        Matrix Ans;
        int n = A.n, m = B.m;
        Ans.n = n, Ans.m = m;
		for(int i =1; i <= n; ++i)
			for(int j = 1; j <= m; ++j)
				for(int k = 1; k <= A.m; ++k)
					Ans.a[i][j] += A.a[i][k] * B.a[k][j];
		return Ans;
    }
};

时间复杂度 O ( n ⋅ m 2 ) O(n \cdot m ^ 2) O(nm2)

i , j , k i, j, k i,j,k 枚举顺序修改对结果无影响,但对速度有影响

i , j , k i,j,k i,j,k 顺序枚举跑 n = 1024 n = 1024 n=1024 ,耗时 9.454 s 9.454s 9.454s

j , k , i j,k,i j,k,i 顺序枚举 14.19 s 14.19s 14.19s

k , j , i k,j,i k,j,i : 12.46 s 12.46s 12.46s

i , k , j i,k,j i,k,j : 5.212 s 5.212s 5.212s

所以循环顺序改成 i , k , j i, k, j i,k,j ( j j j 放在最后一位即可)
利用缓存机制

下午

例1

给定 n , p n, p n,p, 求斐波那契数列第 $ n $ 项 $ \bmod p$

数组 $ f $代表 f i b fib fib 数列

手推可得

$$

\begin{vmatrix}{}
f_{i - 1} & f_{i - 2}
\end{vmatrix}

\times

\begin{vmatrix}{}
1 & 1\
1 & 0
\end{vmatrix}

=
\begin{vmatrix}{}
f_{i} & f_{i - 1}
\end{vmatrix}
$$


B = ∣ 1 1 1 0 ∣ B = \begin{vmatrix}{} 1 & 1\\ 1 & 0 \end{vmatrix} B= 1110
$$
\therefore

\begin{vmatrix}{}
f_{n} & f_{n - 1}
\end{vmatrix}

=

\begin{vmatrix}{}
f_{1} & f_{0}
\end{vmatrix}

\times

B ^ n
$$
矩阵乘法套快速幂即可

#include<bits/stdc++.h>
#define ll long long
#define itn int
#define ull unsigned long long
#define gt getchar
#define pt putchar
using namespace std;
inline ll read(){
   
    ll x=0,f=1;char ch=gt();
    while(!isdigit(ch)){
   if(ch=='-')f=-1;ch=gt();}
    while(isdigit(ch)){
   x=(x<<1)+(x<<3)+(ch^48);ch=gt();}
    return x*f;}
inline void print(ll x){
   if(x<0)pt('-'),x=-x;if(x>9)print(x/10);pt(x%10+48);}
const ll M = 1e9 + 7;
ll n;
struct Matrix{
   
    ll a[105][105];//自行开空间 
    int n, m;
    Matrix() {
   memset(a, 0, sizeof a);}
}ans, base;
Matrix operator * (const Matrix &A, const Matrix &B){
   //& 直接调用A, B.const 防止 A, B 被修改 
        Matrix Ans;
        int n = A.n, m = B.m;
        Ans.n = n, Ans.m = m;
		for(int i =1; i <= n; ++i)
			for(int j = 1; j <= m; ++j)
				for(int k = 1; k <= A.m; ++k)
					Ans.a[i][j] = (Ans.a[i][j] + A.a[i][k] * B.a[k][j]) % M, Ans.a[i][j] %= M;
		return Ans;
    }
void init(){
   
	base.n = base.m = 2, ans.n = 2, ans.m = 2;
    base.a[1][1] = base.a[1][2] = base.a[2][1] = 1;
    ans.a[1][1] = ans.a[1][2] = 1;
}
void qpow(ll b){
   
    while(b){
   
        if(b & 1)ans = ans * base;
        base = base * base;
        b >>= 1;
    }
}
int main(){
   
    n = read();
    if(n <= 2)return puts("1"), 0;
    init();
    qpow(n - 2);
    cout << ans.a[1][1] % M << "\n";
    return 0;
}
例2

一个数列 f i = f i − 1 × f i − 3 f_{i} = f_{i - 1} \times f_{i - 3} fi=fi1×fi3

仍然手推可得

$$

\begin{vmatrix}{}
f_{i - 1} & f_{i - 2} & f_{i - 3}
\end{vmatrix}

\times

\begin{vmatrix}{}
1 & 1 & 0\
0 & 0 & 1\
1 & 0 & 0
\end{vmatrix}

=
\begin{vmatrix}{}
f_{i} & f_{i - 1} & f_{i - 2}
\end{vmatrix}
$$


B = ∣ 1 1 0 0 0 1 1 0 0 ∣ B = \begin{vmatrix}{} 1 & 1 & 0\\ 0 & 0 & 1\\ 1 & 0 & 0 \end{vmatrix} B= 101100010
$$
\therefore

\begin{vmatrix}{}
f_{n} & f_{n - 1} & f_{n - 2}
\end{vmatrix}

=

\begin{vmatrix}{}
f_{2} & f_{1} & f_{0}
\end{vmatrix}

\times

B ^ n
$$

扩展

已知 f i = k 1 f i − 1 + k 2 f i − 2 . . . . + k m f i − m + 1 ( m ≤ i ) f_i=k_{1}f_{i-1}+k_{2}f_{i-2}....+k_{m}f_{i-m+1}(m\le i) fi=k1fi1+k2fi2....+kmfim+1(mi)

f n   m o d   p ( 1 ≤ n ≤ 1 0 18 ) f_n \bmod p(1\le n\le 10^{18}) fnmodp(1n1018)

构造矩阵
$$
\overbrace{
\begin{vmatrix}{}
k_1 &1&0&0&…\
k_{2}&0&1&0&…\
k_{3}&0&0&1&…\
k_{4}&0&0&0&…\
…&…&…&…&…\
k_{m}&0&0&0&…\
\end{vmatrix}}^{\text{m}}

\times

\begin{vmatrix}{}
f_1 &f_2&&f_3&…&f_m\
\end{vmatrix} ^ n
$$

即可得出答案

矩阵乘法中
A × B ≠ B × A A \times B \not = B \times A A×B=B×A

例3

一个有向图, n n n 个点,走 k k k 步, 求从 1 走到 n n n 的方案数

$ n \le 100, k \le 100$

考虑动态规划

b j i , j bj_{i,j} bji,j 表示 i i i j j j 之间是否连边, $ f_{i,j} $ 表示走到 j j j 走了 i i i 步的方案数

所以答案即为 f k , n f_{k,n} fk,n

初始状态为 $ f_{0,1} = 1$

状态转移为 $ f_{i,j} += \sum_{k = 1}^nf_{i-1,k} \times bj_{k,i}$

部分代码:

n = read(), k = read();
for(int i = 1; i <= n; ++i)bj[i][j] = read();
f[0][1] = 1;
for(int a = 1; a <= k; ++a)
	for(int b = 1; b <= n; ++b)
		for(int c = 1; c <= n; ++c){
   
		//走了a步,走到了b,第a - 1步在c
			f[a][b] += f[a - 1][c] * bj[c][b];
		}
cout << f[k][n];

强行加一维

n = read(), k = read();
for(int i = 1; i <= n; ++i)bj[i][j] = read();
f[0][1][1] = 1;
for(int a = 1; a <= k; ++a)
	for(int d = 1; d <= n; ++d)
		for(int b = 1; b <= n; ++b)
			for(int c = 1; c <= n; ++c){
   
			//走了a步,走到了b,第a - 1步在c
				f[a][d][b] += f[a - 1][d][c] * bj[c][b];
			}
cout << f[k][1][n];

提出 f a , d , b + = f a − 1 , d , c × b j c , b f_{a,d,b} += f_{a-1,d,c} \times bj_{c,b} fa,d,b+=fa1,d,c×bjc,b

改为 f a d , b + = f ( a − 1 ) d , c × b j c , b f_{a_{d,b}} += f_{(a-1)_{d,c}} \times bj_{c,b} fad,b+=f(a1)d,c×bjc,b

然后将 f a , f a − 1 , b j f_a, f_{a - 1}, bj fa,fa1,bj当做 n n n n n n 列矩阵

所以 f a = f a − 1 × b j f_a = f_{a - 1} \times bj fa=fa1×bj

即得 f k = f 0 × b j k f_k = f_0 \times bj ^ k fk=f0×bjk

套矩阵快速幂

时间复杂度 O ( log ⁡ n 3 log ⁡ k ) O(\log {n ^ 3 \log k}) O(logn3logk)

例4

LuoguP4159

比例 4 4 4 多了路径长度罢了

把每一条边拆成由几个长度为一的边组成的链

然后由同一个点引出来的链可以合并

代码晚上补(

#include<bits/stdc++.h>
#define ll long long
#define itn int
#define ull unsigned long long
#define gt getchar
#define pt putchar
using namespace std;
inline ll read(){
   
    ll x=0,f=1;char ch=gt();
    while(!isdigit(ch)){
   if(ch=='-')f=-1;ch=gt();}
    while(isdigit(ch)){
   x=(x<<1)+(x<<3)+(ch^48);ch=gt();}
    return x*f;}
inline void print(ll x){
   if(x<0)pt('-'),x=-x;if(x>9)print(x/10);pt(x%10+48);}
const ll M = 2009;
ll n, m;
struct Matrix{
   
    ll a[105][105];//自行开空间 
    int n, m;
    Matrix() {
   memset(a, 0, sizeof a);}
}ans, f;
Matrix operator * (const Matrix &A, const Matrix &B){
   //& 直接调用A, B.const 防止 A, B 被修改 
        Matrix Ans;
        Ans.m = Ans.n = m; 
		for(int i = 1; i <= m; ++i)
			for(int j = 1; j <= m; ++j)
				for(int k = 1; k <= m; ++k)
					Ans.a[i][j] = (Ans.a[i][j] + A.a[i][k] * B.a[k][j]) % M, Ans.a[i][j] %= M;
		return Ans;
    }
void qpow(ll b){
   
    while(b){
   
        if(b & 1)ans = (ans * f);
        f = f * f;
        b >>= 1;
    }
}
int main(){
   
    n = read();int t = read();
    m = 9 * n;
    ans.n = ans.m = f.n = f.m = 9 * n;
    for(int i = 1; i <= n; ++i){
   
    	string s;
    	cin >> s;
    	for(int j = 1; j <= 8; ++j){
   
    		f.a[i + j * n][i + j * n - n] = 1; 
    		ans.a[i + j * n][i + j * n - n] = 1;
		}
		for(int j = 1; j <= n; ++j){
   
			int v = s[j - 1] - '0';
			if(v) f.a[i][j + v * n - n] = 1, ans.a[i][j + v * n - n] = 1;
		}
	}
	qpow(t - 1);
	cout << ans.a[1][n] % M;
    return 0;
}

质数判定

初等数论: 自然数范围

质数 & 素数 & prime : 只有自己和 1 1 1 为因子的数

合数 : 有大于 2 2 2 个因子的数

1既不是素数也不是合数

给定 x x x 判断 x x x 是否为质数

bool is_prime(int x){
	if(x < 2)return 0;
	for(int i = 2; i * i <= x; ++i)
		if(x % i == 0)return 0;
	return 1;
}

O ( x ) O(\sqrt x) O(x )只能处理 x ≤ 1 0 16 x \le 10 ^ {16} x1016

给定 x x x 分解质因数.(不能用筛)

void factorize(int x){
 int cnt = 0;
	for(int a = 2; a * a <= x; ++a){
		if(x % a == 0){
			++cnt;
			prime[cnt] = a;
			while(x % a == 0){
				++num[cnt];
				x /= a;
			}
		}
	}
	if(x != 1){
		++cnt;
		prime[cnt] = x;
		num[cnt] = 1;
	}
}

对于 a a a 第一次合条件时显然一定是质数,然后 x x x 除掉 a a a 直到不能再除,以后的 a a a 符合条件时也一定为质数

而对于特判,意思是如果x本身是质数就需要只输出 x x x 而循环不会记录 x x x 只能特判

例1

现有 1 , 2 , 3 , . . . , n 1, 2, 3, ..., n 1,2,3,...,n n 个数, 将它们分为几组,使得每组内之和都为质数,求最少分几组.

$ n \le 2000$

由哥德巴赫猜想知,大于等于4的偶数,可分为两个质数的和, 大于等于3的奇数,可分为3个质数的和

x = ∑ i = 1 n i = n × ( n + 1 ) 2 x = \sum_{i = 1}^n i = \frac{n \times (n + 1)}{2} x=i=1ni=2n×(n+1)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值