QBXT五一noip数学营

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

Day 1

上午

电脑1s跑3e8()

mod

a/b=c...d,a=bc+d

b>d0

c=a/b,d=amodb

(a+b)modc=(amodc+bmodc)modc

(ab)modc=(amodcbmodc)modc

(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=ag,b=bg

gcd(a,b)=1

gcd(ab,b)=gcd((ab)g,bg)=gcd(ab,b)g=1g=g

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(logn)

证明:
ab,gcd(a,b)=gcd(b,amod b)

amodb<a/2

证明:

分类讨论

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

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

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

所以 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";

时间复杂度:

ans 每次都在变小, 而每次 gcd 都可看做除 2
所以最多除 logans

ans=a1 , 所以 O(n+loga1)

g=gcd(a,b),l=lcm(a,b)

gl=ab

代码

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

快速幂

x,y,p109

例: y=37

x37=(x18)2x

x18=(x9)2

x9=(x4)2x

x4=(x2)2

x2=xx

时间复杂度O(logy)

代码
通用版(循环)
ll qpow(ll a, ll b, ll p){
	if(b == 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;
    if(y == 1)return a;
	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(
矩阵加法与减法

|123321|+|123321|=|444444|

矩阵减法同理

矩阵乘法

A(nm)B(mk)
只有第一个矩阵的列数等于第二个矩阵的行数才行

|123321|×|121212|=|11+21+3112+22+3231+21+1132+22+12|=|612612|

代码:

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(nm2)

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

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

j,k,i 顺序枚举 14.19s

k,j,i : 12.46s

i,k,j : 5.212s

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

下午

例1

给定 n,p, 求斐波那契数列第 nmod p

数组 f 代表 fib 数列

手推可得

|fi1fi2|×|1110|=|fifi1|

B=|1110|

|fnfn1|=|f1f0|×Bn

矩阵乘法套快速幂即可

#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

一个数列 fi=fi1×fi3

仍然手推可得

|fi1fi2fi3|×|110001100|=|fifi1fi2|

B=|110001100|

|fnfn1fn2|=|f2f1f0|×Bn

扩展

已知 fi=k1fi1+k2fi2....+kmfim+1(mi)

fnmodp(1n1018)

构造矩阵

|k1100...k2010...k3001...k4000..................km000...|m×|f1f2f3...fm|n

即可得出答案

矩阵乘法中

A×BB×A

例3

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

n100,k100

考虑动态规划

bji,j 表示 ij 之间是否连边, fi,j 表示走到 j 走了 i 步的方案数

所以答案即为 fk,n

初始状态为 f0,1=1

状态转移为 fi,j+=k=1nfi1,k×bjk,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];

提出 fa,d,b+=fa1,d,c×bjc,b

改为 fad,b+=f(a1)d,c×bjc,b

然后将 fa,fa1,bj 当做 nn 列矩阵

所以 fa=fa1×bj

即得 fk=f0×bjk

套矩阵快速幂

时间复杂度 O(logn3logk)

例4

LuoguP4159

比例 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;
}

质数判定

初等数论: 自然数范围

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

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

1 既不是素数也不是合数

给定 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) 只能处理 x1016

给定 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 第一次合条件时显然一定是质数,然后 x 除掉 a 直到不能再除,以后的 a 符合条件时也一定为质数

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

例1

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

n2000

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

x=i=1ni=n×(n+1)2

x 为偶数,输出 2 ,若 x 为奇数则分类讨论

x2 等于 0 则输出 2 ,否则输出 3

#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);}
bool check(int n){
	if(n <= 1)return 0;
	for(int i = 2; i * i <= n; ++i)if(n % i == 0)return 0;
	return 1;
}
int a[6005];
int main(){
    int n = read();
    int x = n * (n + 1) / 2;
	if(n == 1)return puts("-1"), 0;
	if(n == 2)return puts("1 1"), 0;
	if(n == 3)return puts("1 1 2"), 0;
	for(int i = 1; i <= n; ++i){
		a[i] = 1;
	}
	if(x & 1){
		if(check(x - 2)){
			a[2] = 2;
			for(int i = 1; i <= n; ++i)cout << a[i] << ' ';
		}
		else{
			a[3] = 3;
			for(int i = 1; i <= n; ++i)if(check(i) && check(x - 3 - i)){
				a[i] = 2;
				break;
			}
			for(int i = 1; i <= n; ++i)cout << a[i] << ' ';
		}
	}
	else{
		for(int i = 1; i <= n; ++i)if(check(i) && check(x - i)){
			a[i] = 2;
			break;
		}
		for(int i = 1; i <= n; ++i)cout << a[i] << ' ';
	}
    return 0;
}

Day 2

上午

逆元

(3+4)mod5=2

(34)mod5=4

[(1+7)÷4]mod5=2

(3÷4)mod5=???

逆元:对于 a÷bmodp 找到一个整数 c ,使得 a÷bmodp=a×cmodp

费马小定理:

p 为质数,且 1a<p, ap11(mod p)

即得: ap21a(mod p)

此时 ap2 即为 amod p 意义下的逆元

inline void inv(ll a, ll b, ll p){return 1ll * a * ksm(b, p - 2, p) % p}

这个方法只适合于 p 是质数 ,且 gcd(a,p)=1

欧拉定理:

gcd(a,b)=1 即可

aϕ(p)1(mod p)

ϕ(p) : 欧拉函数,求 1p 中与 p 互质的个数

由欧拉定理可得: aϕ(p)11a(modp)

p 为质数时, ϕ(p)=p2 ,所以费马小定理是欧拉定理的特殊情况.

ϕ(n):

n=p1,ϕ(n)=p11

n=p12,ϕ(n)=p12p1=p1(p11)

n=p1k,ϕ(n)=p1k1(p11)=pi1pin

n=p1p2,ϕ(n)=nnp1np2+np1p2=n(11p1)(11p2)

n=p1k1p2k2p3k3......ptkt

ϕ(n)=n(11p1)(11p2)...(11pt)

ll getphi(ll n){
	ll phi = n;
	for(int i = 2; i* i <= n; ++i){
		if(n % i == 0){
			phi = phi / i * (i - 1);
			while(n % i == 0)
				n = n / i;
		}
	}
	if(n != 1)phi = phi / n * (n - 1);
	return phi;
}

O(n)

例1

LuoguP3811

给定 n,p ,求 1n 中每个数 mod p 意义下的逆元

n106,p 为在 109 附近的质数

法一

先递推 1n 阶乘 fac[i]

然后从 (n1)1 推, 1i!=1(i+1)!(i+1)

求出阶乘逆元 facinv[i]

然后逆元 1i=(i1)!1i!

cin >> n >> p;
fac[0] = 1;//阶乘
for(int i = 1; i <= n; ++i)
	fac[i] = 1ll * fac[i - 1] * i % p;
facinv[n] = ksm(fac[n], p -2, p);//阶乘逆元
for(int i = n - 1; i >= 1; --i)
	facinv[i]= 1ll * facinv[i + 1] * (i + 1) % p;
for(int i =1; i <= n; ++i)//逆元
	inv[i] = 1ll * fac[i - 1] * facinv[i] % p;
法二

假设 inv1invi1 都已经求出, invi=?

p>ip=ki+r

所以 0ki+r(mod p)

rki(mod p)

rik(mod p)

1ikr(mod p) 即可得出.

cin >> n >> p;
inv[1] = 1;
for(int i = 2; i <= n; ++i){
	int k = p / i, r = p % i;
	//p = k * i + r
	//0 = k * i + r(mod p)
	//-r = k * i(mod p)
	//1 / i = -k / r(mod p)
	inv[i] = 1ll * (p - k) * inv[r] % p;
}

质数

n 为质数, n1=d×2r

有一个 a ,1a<n 使得

  1. admodn=1

  2. 存在 0i<r, ad×2imodn=n1

n 若为质数, 则两个性质中至少满足一个,若都不满足,则一定不为质数

随机代入 a 检验, 满足的次数越多,则是质数的概率越大,有一个 a 不满足条件,则一定不是质数

法一

//n - 1 = d * 2 ^ r
//找一个 1 <= a <= n
//1. a ^ d % n = 1
//2. 存在0 <= i < r使得a ^ (d * 2 ^ i) % n = n - 1
//当n是质数时,至少一条成立
bool miller_rabin(int n, int a){
	int d = n - 1, r = 0;
	while(d % 2 == 0)d /= 2, ++r;
	int x = ksm(a, d, r);
	if(x == 1)return 1;
	for(int i = 0; i < r; ++i){
		if(x == n - 1)return 1;
		x = 1ll * x * x * % n;
	}
	return 0;
}//O(logn)
bool check(int n){
	if(n < 2)return 0;
    for(int i = 1; i <= 23; ++i){
    	int a = rand()%(n - 1) + 1;
        if(!miller_rabin(n, a)) return 0;
    }
    return 1;
}

法二

bool miller_rabin(int n, int a){
	int d = n - 1, r = 0;
	while(d % 2 == 0)d /= 2, ++r;
	int x = ksm(a, d, r);
	if(x == 1)return 1;
	for(int i = 0; i < r; ++i){
		if(x == n - 1)return 1;
		x = 1ll * x * x * % n;
	}
	return 0;
}//O(logn)
int p[] = {2, 3, 5, 7, 11, 13, 23, 37, 73};
bool check(int n){
	if(n < 2)return 0;
    for(int i = 0; i < 8; ++i){
    	if(n == p[i])return 1;
        if(n % p[i] == 0) return 0;
        if(!miller_rabin(n, p[i] % n))reutrn 0;
    }
    return 1;
}

exgcd

方程 xa+yb=g(gcd(a,b)=g)

gcd(b,amodb)=gxb+y(amodb)=g

xb+yaybab=g

ya+(xyab)b=g

int exgcd(int a, int b, int &x, int &y){
	//求g = gcd(a, b)以及 xa + yb = g
    if(!b){x = 1, y = 0; return 0;}
    int xp, yp;
    int g = exgcd(b, a % b, xp, yp);
    //xp * b + yp * (a % b) = g
    //xp * b + yp * (a - b * (a / b)) = g
    //xp * b + yp * a - yp * b * (a / b) = g
    //yp * a + (xp - yp *(a/b)) * b = g
  	x = yp;y = xp - yp * (a / b);
    return g;
}

裴蜀定理

对于 xa+yb(x,yZ) 能表示的最小正整数为 gcd(a,b)

例1

LuoguP1082

ax1(modb)ax=yb+1

axyb=1

变为 ax+by=gcd(a,b)

#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);}
ll n, p ,x, y;
void exgcd(ll a, ll b){
    if(!b)x = 1, y = 0;
    else exgcd(b, a % b), swap(x, y), y -= a / b * x;
}
int main(){
    n = read(), p = read();
    exgcd(n, p);
    cout << (x % p + p) % p << "\n";
    return 0;
}

下午

例2

中国剩余定理(CRT)/(ExCRT)

LuoguP1495

LuoguP4777

法一(大数翻倍):

首先,对于 x1(mod 3),x1(mod 5)

t=lcm(3,5)

(1+t)1(mod 3),(1+t)1(mod 5)

t0(mod 15)

发现 2 个同余方程可以合并:

x{a1(modp1)a2(modp2)

如何合并成 xa(modp):

x=a1, 不断加上 p1 , 次数加过 p2 , 则无解

最后,p=lcm(p1,p2)a=x ,合并完毕,时间复杂度 O(p2)

void solve(int p1, int a1, int p2, int a2, int &p, int &a){
    if(p1 < p2)swap(p1, p2),swap(a1, a2);//优化为O(min(p1, p2))
    int x = a1, g = gcd(p1, p2), l = p1 / g * p2;
    while(x <= l && x % p2 != a2)x += p1;
    if(x > l)p = a = -1;
    else p = l, a = x;
}

法二(ExGcd):

xmodp1=a1,xmodp2=a2,xmodp=a

x=k1×p1+a1=k2×p2+a2

k1×p1k2×p2=a2a1

void solve(int p1, int a1, int p2, int a2, int &p, int &a){
    int g, k1, k2;
    g = exgcd(p1, p2, k1, k2);
    k2 = -k2;
    if((a2 - a1) % g != 0)p = -1, a = -1;
    else{
    int k = (a2 - a1) / g;
        k1 = k1 * k, k2 = k2 * k;
        int x = k1 * p1 + a1;
        p = p1 / g * p2;
        a = (x % p + p) % p;
    }
}

大数翻倍完整代码:

#include<bits/stdc++.h>
#define ll __int128
#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);}

long long a[100005], p[100005];
int n;
ll exgcd(ll a, ll b, ll &x, ll &y){
    if(!b){x = 1, y = 0; return a;}
    ll d = exgcd(b, a % b, y, x); y -= (a / b) * x;
    return d;
}
ll gcd(ll a, ll b){return a ? gcd(b % a, a) : b;}
void solve(ll p1, ll a1, ll p2, ll a2, ll &p, ll &a){
    if(p1 < p2)swap(p1, p2),swap(a1, a2);//优化为O(min(p1, p2))
    ll x = a1, g = gcd(p1, p2), l = p1 / g * p2;
    while(x <= l && x % p2 != a2)x += p1;
    if(x > l)p = a = -1;
    else p = l, a = x;
}
int main(){
    n = read();
    for(int i = 1; i <= n; ++i)p[i] = read(), a[i] = read(), a[i] %= p[i];
    for(int i = 2; i <= n; ++i){
    	ll ta, tp;
    	solve(p[i - 1], a[i - 1], p[i], a[i], tp, ta);
    	if(tp != -1 && ta != -1)p[i] = tp, a[i] = ta;
	}
	cout << a[n];
    return 0;
}

筛法

给定 n 个数,从小到大输出 1n 中的质数

暴力,枚举每个数 O(nn)

miller_rabin ,枚举每个数 O(nlogn)

埃氏筛:

for(int a = 2; a <= n; ++a)
    for(int b = a + a; b <= n; b += a)
        nop[b] = 1;

for(int a = 2; a <= n; ++a)
    if(nop[a] == 0)p[++cnt] = a;

复杂度为 O(n1+n2+...nn)

=n(11+12+...1n)

调和级数, O(nlogn)

优化:

for(int a = 2; a <= n; ++a)
    if(nop[a] == 0)
    	for(int b = a + a; b <= n; b += a)
        	nop[b] = 1;

复杂度 O(nloglogn)

线性筛&欧拉筛

void Ola(){
	for(int i = 2; i <= n; ++i){
		if(nop[i] == 0)p[++cnt] = i;
		for(int j = 1; j <= cnt; ++j){//筛掉第j个质数的i倍
			int x = p[j] * i;
			if(x > n)break;
			nop[x] = 1;
			if(i % p[j] == 0)break;//保证每个数只被最小质因子筛掉
		}
	}
}

时间复杂度 O(n)

积性函数

积性函数:
gcd(a,b)=1 时, f(a)f(b)=f(ab)

例子: ϕ(x)

ϕ(n)=n(11p1)(11p2)...(11pt)

n=p1k1p2k2...ptkt

ϕ(m)=m(11p1)(11p2)...(11pw)

m=p1r1p2r2...pwrw

nm=p1k1p2k2...ptktp1r1p2r2...pwrw

ϕ(nm)=n(11p1)(11p2)...(11pt)m(11p1)(11p2)...(11pw)

ϕ(n)ϕ(m)=ϕ(nm)

线性预处理 ϕ(x)

void Ola(){
    for(int i = 2; i <= n; ++i){
        if(nop[i] == 0)p[++cnt] = i, phi[i] = i - 1;
        for(int j = 1; j <= cnt; ++j){
            int x = p[j] * i;
            if(x > n)break;
            nop[x] = 1;
            phi[x] = phi[p[j]] * phi[i];
            if(i % p[j] == 0){
            	phi[x] = p[j] * phi[i];
                break;
            }
        }
    }
}

Day 3

上午

baby step giant step算法(北上广深算法)

给定 a,b,p 求解 axmodp=b

a,b,p109

暴力容易写出 O(x)

//此文章第1145行(
int solve (int a, int b, int p){
    ll v = 1;
    for(int x = 0; ; ++x){
    if(v == b)return x;
        v = 1ll * v * a % p;
        f(v == 1)return -1;
    }
}

由费马小定理知:

ap11(modp)

所以

ap1+k=ap1×akak(modp)

可得循环节只有 p1 的长度

修改为 O(p1)

int solve (int a, int b, int p){
    ll v = 1;
    for(int x = 0; x < p - 1; ++x){
    if(v == b)return x;
        v = 1ll * v * a % p;
        f(v == 1)return -1;
    }
}

bsgs 解法:

先将 a0ap1 分为几组, 每组为

a0,a1,...as1

as,as1,...,a2s1

a2s......

...

查看第一组是否有解,若有,则跳出循环,否则查看第二组......

和暴力复杂度其实一样

我们可以发现如果 b 在第二组中出现,则一定在第一组中有 bas 出现,

如果 b 在第二组中出现,则一定在第一组中有 ba2s 出现

...

所以可以检查第一组得到解, 有分块的思想

代码

int solve (int a, int b, int p){
	int s = sqrt(p);
	int v = 1;
	set<int> se;//集合STL
	for(int i = 0; i < s; ++i){
		se.insert(v);
		v = 1ll * v * a % p;
	}
	//O(p/s)
	for(int i = 0; i * s <= p; ++i){//检查答案是否在第i行 
	//看b * a ^ (-i* s) 是否在第0行出现
		int c = 1ll * b * ksm(ksm(a, i * s, p), p - 2, p) % p;
		if(se.count(c) != 0){
			int v = ksm(a, i * s, p);//第i行第一个数
			for(int j = i * s; ; ++j){//最坏O(s)
				if(v == b)return j;
				v = 1ll * v * a % p;
			} 
		}
	} 
	//总 : O(max(s, p/s)); 即得s = sqrt(p)最合适 
	//所以时间复杂度 O(sqrt(p)) 
	return -1;
}
//1234行(

时间复杂度 O(p) (分块复杂度

例1

LuoguP3846
模板,见代码:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define gt getchar
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;}
ll ksm(ll a, ll b, ll p){
	if(b == 0)return 1; 
	ll ans = 1;
	while(b){
		if(b&1)ans = ans * a % p;
		b >>= 1;
		a = a * a % p; 
	}
	return ans;
}
ll solve(ll a, ll b, ll p){
	int s = sqrt(p);
	int v = 1;
	set<int> se;
	for(int i = 0; i < s; ++i){
		se.insert(v);
		v = 1ll * v * a % p;
	}
	for(int i = 0; i * s <= p; ++i){
		int c = 1ll * b * ksm(ksm(a, i * s, p), p - 2, p) % p;
		if(se.count(c) != 0){
			int v = ksm(a, i * s, p);
			for(int j = i * s; ; ++j){
				if(v == b)return j;
				v = 1ll * v * a % p;
			}
		}
	}
	return -1;
}
int main(){
	int p, b, n;
	cin >> p >> b >> n;
	ll ans = solve(b, n, p);
	if(ans == -1)cout << "no solution";
	else cout << ans;
	return 0;
}

组合数学

组合睡学(

核心: 计数, 方案数

加法原理 : 从 AB 上面 3 条路,下面 2 条路,总共 5 种方案 (同一阶段内加)

乘法原理 ; 从 AB3 条路, BC2 条路, AC6 种方案 (不同阶段内乘)

排列: n 个人选 m 个人, 考虑 m 个人的顺序

1 个人有 n 种选法

2 个人有 n1 种选法

m 个人有 nm+1 种选法,

P(nm)=n(n1)(n2)...(nm+1)=n!(nm)!

组合: n 个人选 m 个人, 不考虑 m 个人的顺序

C(nm)=n(n1)...(nm+1)m!=n!(nm)!m!=P(nm)m!

C(nm)=n!(nm)!m!

组合数一些式子:

  1. 一个都不选或者全选,只有一种做法:

    (n0)=1=(nn)

  2. m 个或者选 nm 个丢掉实质一样:

    (nm)=(nnm)

  3. m 个, 考虑分情况讨论::

    1. 选第 1 个物品, 还需要从剩下的 n1 中选 m1

    2. 不选第 1 个物品, 还需要从剩下的 n1 中选 m

    (nm)=(n1m1)+(n1m)

    预处理求所有的组合数

for(int i = 0; i <= n; ++i){
    c[i][0] = 1;
    for(int j = 1; j <= i; ++j)
        c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
}

此时的 C 数组是个杨辉三角形

1

1,1

1,2,1

1,3,3,1

1,4,6,4,1

  1. 选任意多个物品:

    (n0)+(n1)+(n2)+...+(nn)=2n

  2. n1

    (n0)(n1)+(n2)(n3)+...±(nn)=0

    由第 3 个式子可得,所有奇数位置的和等于杨辉三角中上一行的和,所有偶数位置的和也等于杨辉三角上一行的和都等于 2n1

    所以奇数位置之和与偶数位置之和之差为 0

  3. (x+y)n:

(x+y)0=1

(x+y)1=x+y

(x+y)2=x2+2xy+y2

(x+y)3=x3+3x2y+3xy2+y3

(x+y)4=x4+4x3y+6x2y2+4xy3+x4

观察系数,发现是杨辉三角

(x+y)n=(n0)xny0+(n1)xn1y1+...+(nn)x0yn

即为

(x+y)n=i=0n(ni)xniyi

  1. 3 式的不断展开

(nm)

=(n1m1)+(n1m)

=(n2m2)+2(n2m1)+(n2m)

=(n3m3)+3(n3m1)+3(n3m2)+(n3m)

......

=i=0k(ki)(nkmi)

例1

n 个数, 选 m 个, 不计顺序, 但是一个数可以选任意多次,求方案数

考虑原版,每个数只能选一次其实就是求不等式

1a1<a2<...<amn

的解的个数

这道题可以看做

1b1b2...bmn

的解的个数

c1=b1,c2=b2+1...,cm=bm+m1

就可以看做

1c1<c2<...<cmn+m+1

的解的个数 =(n+m1m)

ans=(n+m1m)

例2

(nm)modp

1.n,m1018,p=1

ans=1

2.n,m1000,p 随意

用杨辉三角递推O(nm)

for(int i = 0; i <= n; ++i){
    c[i][0] = 1;
    for(int j = 1; j <= i; ++j)
        c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % P;
}
cout << c[n][m];

LuoguP2822

代码下课补(

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define gt getchar
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;}
ll t, k;
ll c[2005][2005], qzh[2005][2005];
int main(){
	t = read(), k = read();
	memset(c, -1, sizeof c);
	for(int i = 0; i <= 2000; ++i){
		c[i][0] = 1;
		for(int j = 1; j <= i; ++j){
			c[i][j] = (max(c[i - 1][j - 1], (ll)0) + max(c[i - 1][j],(ll)0)) % k;
		}
	}
	for(int i = 1; i <= 2000; ++i){
		for(int j = 1; j <= 2000; ++j){
			qzh[i][j] = qzh[i - 1][j] + qzh[i][j - 1] - qzh[i - 1][j - 1];
			if(c[i][j] == 0)qzh[i][j]++;
		}
	}
	for(int i = 1; i <= t; ++i){
		int n = read(), m = read();
		cout << qzh[n][m] << "\n";
	}
	return 0;
}

3.n,m106,p 为质数

逆元 O(nlogp)

(nm)=n!(nm)!m!=n![(nm)!]p2(m!)p2modp

fac[0] = 1;
for(int i = 1; i <= 1000000; ++i)fac[i] = 1ll * fac[i - 1] * i % p;
cout << fac[n] * ksm(fac[m], p - 2, p) % p * ksm(fac[n - m], p - 2, p) % p;

下午

4.n109,m103

(nm)=n!(nm)!m!

=n(n1)(n2)..(nm+1)1×2×3×...×m

然后把分母约为 1, 分子乘起来即可 O(m2logm)

for(int i = 1; i <= m; ++i){
    fenzi[i] - i;
    fenmu[i] - n - i + 1; 
}
for(int i = 1; i <= m; ++i){
    for(int j = 1; j <= m; ++j){
        int g = gcd(fenzi[i], fenmu[j]);
        fenzi[i] /= g;
        fenmu[j] /= g;
    }
} 
int ans =1;
for(int i = 1; i <= m; ++i)ans = 1ll * fenzi[i] % p;

5.n,m109,p100 为指数

Lucas 定理:

先将 n,m 转换为 p 进制.

短除法转换进制

n=25,m=12,p=3 ,

n(3)=221,m(3)=110

(2512)modp=(n1m1)(n2m2)(n3m3)modp

int lucas(int n, int m, int p){
    while(n){
        ++x[0];
        x[x[0]] = n % p;
        n = n / p;
    }//n的p进制表示 
    while(m){
        ++y[0];
        y[y[0]] = m % p;
        m = m / p;
    }
    int ans = 1;
    for(int i = 1; i <= x[0]; ++i)
        ans = 1ll * ans * c[x[i]][y[i]] % p;
    return ans; 
}
int main(){
    cin >> n >> m >> p;
    for(int i = 0; i <= p; ++i){
    	c[i][0] = 1;
    	for(int j = 1; j <= i; ++j){
        	c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % p;
		}
	}
	return 0;
}

O(logn)

LuoguP3807

卢卡斯定理只适用于 p 为质数, 当 p 不为质数时, 将其质因数分解

然后中国剩余定理合并, 计算可得

例3

x 拆为 k 个不同组合数之和

x109,k103

酱汁构造(

x=1+1+1+...+1k1+(nk+1)

=(10)+(20)+....+(k10)+(nk+11)

例4

比较 (n1m1)(n2m2) 大小

log(nm)=logn!(nm)!m!=logn!log(nm)!logm!

logn!=log1+log2+...+logn

fac[0] = 0;
for(int i  =1; i <= 1000000; ++i){
	fac[i] = fac[i - 1] + log(i); 
}
double logcnm(int n, int m){
	return fac[n] - fac[m] - fac[n - m];
}

例5

找到 k 个不同的组合数,使得这 k 个组合数之和最大

对于每个组合数 (ab),a,bn

观察杨辉三角

1

1,1

1,2,1

1,3,3,1

1,4,6,4,1

最底层中间的数最大,
往外扩散逐渐减小

单调队列, 结构体重载运算符

代码晚上补(

例6

LuoguP3746

f(n,r)=i=0infC(nk,ik+r)

展开 k

=i=0infj=0kC(k,j)C(nkk,ik+rj)

=j=0ki=0infC(k,j)C(nkk,ik+rj)

=j=0ki=0infC(k,j)C(nkk,ik+rj)

=j=0kC(k,j)i=0infC((n1)k,ik+rj)

=j=0kC(k,j)f(n1,rj)

D(rj,r)=C(k,j)

fn(1,r)=j=0kfn1(1,rj)D(rj,r)

矩阵快速幂

代码晚上能补出来?(

#include<bitsdc++.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);}
ll n, p, k, r;
struct Matrix{
    ll a[55][55];//自行开空间 
    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;
        Ans.n = A.n, Ans.m = B.m;
		for(int i = 0; i < A.n; ++i)
			for(int j = 0; j < B.m; ++j)
				for(int k = 0; k < A.m; ++k)
					Ans.a[i][j] = (Ans.a[i][j] + A.a[i][k] % p * B.a[k][j] % p) % p, Ans.a[i][j] %= p;
		return Ans;
    }
void ksm(ll b){
    while(b){
        if(b & 1)ans = ans * base;
        base = base * base;
        b >>= 1;
    }
}
void init(){    
    ans.a[0][0] = 1;
    ans.n = base.n = base.m = k, ans.m = k;
    for(int i = 0; i < k; ++i){
        base.a[i][i] = 1;
        base.a[i][(i + 1) % k]++;
    }
}
int main(){
    n = read(), p = read(), k = read(), r = read();
    init();
    ksm(n * k);
    cout << ans.a[0][r];
    return 0;
}

抽屉原理

n+1 个东西放入 n 个抽屉里,至少有 1 个抽屉有至少 2 个东西

kn+1 个东西,放入 n 个抽屉里, 至少有一个抽屉有至少 k+1个东西

例1

给定 n 个数, 要求从中选出任意多个数, 使得它们的和为 c 的倍数

cn105

用前缀和做:

qzh 数组中存放 1+n 个数: qzh0,qzh1,...,qzhn

将每个元素 mod c 后的结果分类 0,1,2,...,c1

据抽屉原理知一定有同类的结果, 即得答案

代码等补(

例2

平面上 n 个点 (xi,yi)

用三个大小为 L×L 的正方形覆盖住所有点

求最小的 L

显然二分答案, 如何写 check 函数

得到这些点 x,y 的最大最小值,用最小的矩形框住所有点

先用一个正方形盖住一角, 将剩下的点继续用最小矩形框住,再用一个正方形盖住一角,最后检查剩下的点能否盖住即可.

check中循环 4×4.

O(4×4×n)

代码晚上补啊啊啊啊啊啊啊啊啊啊

容斥

A1 为学语文的人的集合, A2 为学数学的人的集合, A3 为学英语的人

同时学语文和数学的人: A1A2

学语文或数学的人: A1A2

|A1A2|=|A1|+|A2||A1A2|

|A1A2A3|=|A1|+|A2|+|A3||A1A2||A2A3||A1A3|+|A1A2A3|

|A1A2A3A4|

=|A1|+|A2|+|A3|+|A4|

|A1A2||A1A3||A1A4||A2A3||A2A4||A3A4|

+|A1A2A3|+|A1A2A4|+|A1A3A4|+|A2A3A4|

|A1A2A3A4|

例1

n 对夫妻, 2n 个人,围成一个环,求方案数.

  1. 夫妻不能相邻

  2. 旋转算同一种方案

先不考虑要求1,并且坐成一排,答案 (2n)!

Day 4

上午

坐成一圈, 答案 (2n)!2n1=(2n1)!

这样,所有不合法方案一定被减掉,但是, 有可能减多次

(n1)(2n2)!2 :减去使一对夫妻强制相邻的方案数

+(n2)(2n3)!22:加上两对夫妻强制相邻的方案数

(n3)(2n4)!23:加上三对夫妻强制相邻的方案数

剩下同上

总式:

ans=i=0n(ni)(2ni1)!2i(1)i

容斥通用做法:

要满足 n 个条件, 先减满足前 1 个条件的方案数, 再加满足前 2 个条件的方案数.......

例2

询问 1n 中有多少数能表示成 xy,y>1 的形式

n1018

1n 中能表示成 x2xn

1n 中能表示成 x3xn3

1n 中能表示成 xyxny

y6=(y2)3=(y3)2

所以要减去 n6 保证不重

#include<cmath>

cin >> n;
for(int a = 2; a <= 64; ++a){
	num[a] = 0;//代表x^a 这种形式的数算了几次 
}
for(int a = 2; a <= 64; ++a){
	//pow(x, y) = x ^ y;
	//pow(x, 0.5) = sqrt(x);
	ll v = floor(powl(n, 1.0 / a)) - 1;
	int d = 1 - num[a];
	ans += v * d;
	num[a] += d;
	for(int b = a; b <= 64; b += a){
		num[b] += d;
	} 
} 
cout << ans + 1;//补上1 

LuoguP9118

矩阵

解方程:

{x1+x2=2x1+x2=4

消元即可

如何求 n 元一次方程

{a1,1x1+a1,2x2+...+a1,nxn=b1(1)a2,1x1+a2,2x2+...+a2,nxn=b2(2)......an,1x1+an,2x2+...+an,nxn=bn(n)

高斯消元

(2)(1)a2,1a1,1

(3)(1)a3,1a1,1

......

消掉 x1

剩下 n1 个方程和未知数.....

再不断消掉其他的位置数,只剩一个未知数和方程,然后回带

{x1+x2+x3=32x1+3x2+4x3=93x1+7x2+10x3=20

变成

{x1+x2+x3=30+x2+2x3=60+4x2+7x3=11

变成

{x1+x2+x3=30+x2+2x3=60+01x3=1

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
double a[105][105];
int n;
double x[105];
void Gauss(){
	for(int i = 1; i <= n; ++i){
		//把xi从第i+1到第n个方程中消掉
		for(int j = i; j <= n; ++j){
			if(fabs(a[j][i]) > fabs(a[i][i])){//主元消元法
				swap(a[i],a[j]);
				break;
			}
		}
		for(int j = i + 1; j <= n; ++j){//把xi从第j个方程中消掉 
			double ratio = a[j][i] / a[i][i];
			for(int k = 1; k <= n + 1; ++k){
				a[j][k] -= a[i][k] * ratio;
			} 
		} 
	}
	for(int i = n; i >= 1; --i){
		for(int j = i + 1; j <= n; ++j)
			a[i][n + 1] -= a[i][j] * x[j]; 
		x[i] = a[i][n + 1] / a[i][i];
	}
}
int main(){
	cin >> n;
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j)
			cin >> a[i][j];
		cin >> a[i][n + 1];
	}
	Gauss();
	//a[1][1] * x[1] + a[1][2] * x[2] + ... + a[1][n] * x[n] = a[1][n + 1];
	//a[2][1] * x[1] + a[2][2] * x[2] + ... + a[2][n] * x[n] = a[2][n + 1];
	// ............................................................
	//a[n][1] * x[1] + a[n][2] * x[2] + ... + a[n][n] * x[n] = b[n][n + 1];
	for(int i = 1; i <= n; ++i){
		printf("%.2lf\n", x[i]);
	}
	return 0;
}


O(n3)

可以用最小公倍数做, 避免精度问题

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int a[105][105];
int n;
double x[105];
void Gauss(){
	for(int i = 1; i <= n; ++i){
		//把xi从第i+1到第n个方程中消掉
		for(int j = i; j <= n; ++j){
			if(fabs(a[i][j] != 0)){//实数判断是否为0 
				swap(a[i],a[j]);
				break;
			}
		}
		for(int j = i + 1; j <= n; ++j){//把xi从第j个方程中消掉 
			if(a[j][i] == 0)continue;
			int l = a[i][i] / gcd(abs(a[i][i]),abs(a[j][i])) * a[j][i];
			int ratioi = l / a[i][i];
			int ratioj = l / a[j][i];
			for(int k = 1; k <= n + 1; ++k)
				a[j][k] = a[j][k] * ratioj - a[i][k] * ratioi;
		} 
	}
	for(int i = n; i >= 1; --i){
		for(int j = i + 1; j <= n; ++j)
			a[i][n + 1] -= a[i][j] * x[j]; 
		x[i] = (double)a[i][n + 1] / a[i][i];
	}
}
int main(){
	cin >> n;
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j)
			cin >> a[i][j];
		cin >> a[i][n + 1];
	}
	Gauss();
	//a[1][1] * x[1] + a[1][2] * x[2] + ... + a[1][n] * x[n] = a[1][n + 1];
	//a[2][1] * x[1] + a[2][2] * x[2] + ... + a[2][n] * x[n] = a[2][n + 1];
	// ............................................................
	//a[n][1] * x[1] + a[n][2] * x[2] + ... + a[n][n] * x[n] = b[n][n + 1];
	for(int i = 1; i <= n; ++i){
		printf("%.2lf\n", x[i]);
	}
	return 0;
}

单位矩阵 I :

I=|100...0010...0001...0...............000...1|

AI=IA

AB=I

BA 的倒数

AB=CCi,j=k=1nAi,kBk,j

就有 n2 个未知数和方程, 高斯消元求解 B, 时间复杂度 O(n6)

A=|1234|

A×|0110|=|2143|

|0110|×A=|3412|

|1000001001000001|×|1234234134124123|=|1234341223414123|

所以矩阵可以高斯消元消成单位矩阵,在消元过程中相当于矩阵乘 B

下午

概率和期望

事件

扔一个骰子

样本空间: 1,2,3,4,5,6

A 事件中, 1,2,3 为样本点

A={1,2,3}

样本点组合成事件

P(A)=12

A={1,2,3},B={2,3,4}

AB={1,2,3,4}

AB={2,3}

减法: 把 A 中出现在 B 中的元素删掉

AB={1}=AAB

概率

概率定义:为样本空间的每一个事件定义一个实数,这个实数称为概率

事件的概率等于它所包含的样本点的概率之和

0P(A)1

n 个样本点: B1,B2,...,Bn

P(B1)+P(B2)+..+P(Bn)=1

P(A)=0 : 不可能事件

P(A)=1 : 必然事件

P(A|B) : 条件概率: B 发生的情况下 A 发生的概率

例子:

1,2,3,4,5,6

A={1},B={1,2,3}

P(A|B)=13

C={1,2,3},D={1,3,5}

P(C|D)=23

公式1: P(A|B)=P(AB)P(B)

P(C|D)=P(CD)P(D)=1312=23

公式2: P(A|B)P(B)=P(AB)

独立事件 : 两个事件 A,B,A 的发生不影响 B 的发生, B 也不影响 A

即: P(A)P(B)=P(AB),P(A)=P(A|B)

扔两次骰子,第一次与第二次互相不影响,互为独立事件

期望

扔骰子: 1,2,3,4,5,6

数的期望: 1,2,3,4,5,6

概率:16,16,16,16,16,16

期望值: 1×16+2×16+3×16+4×16+5×16+6×16=72

数的平方的期望 1,4,9,16,25,36

此时的期望值 916

E[f(X)]=f(X)P(X=x)

期望的和 等于和 的期望

E[x1+x2]=E[x1]+E[x2]

E[x1+x12]=E[x1]+E[x12]

例1

三张一样的卡, 其中一张两面黑,一张两面红,一张一面红一面黑

随机抽取一张放在桌子上,红色面朝上,另一面为黑色的概率是多少是多少

因为有三张牌,而又分正反面,所以朝上的情况有 6

1R,2R,3R,4B,5B,6B

P(下黑|上红)=P(下黑上红)P(上红)=13

例2

n 个人抽签, 有人抽中则立即停止抽签, 问是否公平

公平.

第一个人的概率是: 1n

第二个人的概率是: n1n1n1=1n

第三个人的概率是: n1nn2n11n2=1n

以此类推

例3

设男女人口比例为 51:49, 男性色盲率为 2%, 女性色盲率为 0.25%

P(男|盲)=P(男,盲)P()=51%2%51%2%+49%0.25%

例4

一个人左右口袋各一盒火柴,每盒 n 支,每次抽烟时随机选一盒点火,由于习惯, 选右口袋的概率大于二分之一 (P>12) ,, 问下面两种情形概率是否相等,试求概率值

  1. 某次发现取的这一盒已经空了, 这是另一盒恰好有 m 支火柴

  2. 某次用完一盒时另一盒恰好有 m 盒火柴

概率
1p n n+1
p n nm

当右边先取完,p(n+1)(1p)(nm)

当左边先取完,(1p)(n+1)p(nm)C(2nm,n)

例5

在小葱和小泽面前有三瓶药,其中有两瓶是毒药,每个人必须喝
一瓶。小葱和小泽各自选了一瓶药,小泽手速比较快将药喝了下去,然
后就挂掉了.。小葱想活下去,他是应该喝掉手上的这瓶,还是另外剩下的一瓶
呢?

其实是三门问题.

有三扇门, 两个门后面是羊,一个门后面是车,你选定了一个门,主持人打开了一个门显示为羊

1 2 3

假设你选第一个门 (p=13), 主持人不论打开剩下的那扇门都应该不换,选第二个门 (p=13), 主持人打开第 3 个门, 应该换, 选第三个门 (p=13), 主持人打开第 2 个门,应该换.

所以 P()=23

1 2 3

小泽喝药死了是随机行为,不一定是因为毒药

所以换不换都一样

例6

过河有两个路

  1. 100 个石头,有 1100 的概率挂掉
  2. 1000 个石头,有 11000 的概率挂掉

问选哪个.

首先,选 1 活的概率是 (99100)100
2 活的概率是 (9991000)1000

11000<1999

111000>11999

9991000>998999

9991000>998999>...>990991

99100=9901000=9991000998999...990991

99100<(9991000)100,99100<9991000

(99100)100<(9991000)100

(99100)100<(9991000)1000

例7

小胡站在原点,手里拿着两枚硬币。抛第一枚硬币正面向上的概
率为 q,第二枚正面向上的概率为 r

小胡开始抛第一枚硬币,每次抛到反面小胡就向 y 轴正方向走一
步,直到抛到正面。

接下来小胡继续抛第一枚硬币,每次抛到反面小胡就向 x 轴正方
向走一步,直到抛到正面。

现在小胡想回来了,于是他开始抛第二枚硬币,如果小胡抛到正
面小胡就向 y 轴的负方向走一步,否则小胡就向 x 轴的负方向走一
步。

现在小胡想知道他在往回走的时候经过原点的概率是多少呢?

x=0infy=0inf(1p)xp1走到(x,0)(1p)yp2走到(x,y)qx(1q)y(x+yx)回到(0,0)

=x=0infy=0infp2(ip)x+yqx(iq)y(x+yx)

t=x+y

=t=0infx=0tp2(ip)tqx(iq)tx(tx)

=p2t=0inf(ip)tx=0tqx(iq)tx(tx)

据二项式定理:

(a+b)n=x=0naxb(nx)(nx)

得原式

=p2t=0inf(ip)t(q+1q)t

=p2t=0inf(ip)t

发现此时的西格玛为等比数列, 运用等比数列求和公式

原式

=p21(1p)inf+11(1p)

而分子 1(1p)inf+1 无限趋近于 1

原式=p21p=p

例8

检验矩阵 A×B=C 是否成立

n1000

竟然是MILLER_RABIN!

随机一个 n×1 的矩阵 D

A×(B×D)=C×D

只要随机足够多的矩阵 D, 则一定正确.

O(n2)

完结撒花!!!!!!

posted @   Qinzh  阅读(210)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示