抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

组合数

图片
都是一些定义吧。。。
图片
性质1.1.,从nn个东西中选kk个选和选nkn-k个不选是一样的。
性质2.2.,从n+1n+1个东西中选kk个,可以在其中nn个东西中选kk个,剩下一个不选,也可以在其中nn个东西中选k1k-1个,剩下一个选。
性质2.2.在预处理组合数时经常用到,如:

1
2
3
4
5
6
for (register int i=0;i<MAXN;++i){
C[i][0]=1,C[i][i]=1;
for (register int j=1;j<i;++j){
C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
}
}

还有一个神奇的性质,我们把(x+a)n(x+a)^n拆开,变成(x+a)(x+a)....(x+a)(x+a)....发现xx的系数为kk时,相当于在nnxx中选kk个,剩下nkn-k个选aa所以系数为CnkC_n^k,所以,(x+a)n=k=0nCnkxkank(x+a)^n=\sum^n_{k=0}C^k_nx^ka^{n-k}
根据这个性质,我们把x=1x=1a=1a=1代入,发现i=0nCni=(1+1)n=2n\sum^n_{i=0}C^i_n=(1+1)^n=2^n
x=1x=1a=1a=-1代入,发现i=0n(1)iCni=(1+(1))n=0\sum^n_{i=0}(-1)^iC^i_n=(1+(-1))^n=0

剩下的性质,自己脑补一些场景,也可以证明出来。

卡特兰数

图片

所有奇卡特兰数,下标都满足n=2k1n=2^k-1(不知道有啥用,手动狗头)

图片

66点基本上都是老生常谈了,长方形填充还是挺巧妙的。

图片

考虑一个可行的方案,必有如图的一个长方形,它的一个顶点在阶梯上,另一个在阶梯的最下面的角上(标成红色),要不然整个长方形不可能填充完。

发现它上方和右方的小阶梯可以构成子状态,yyyy一下,发现F(n)=i=0n1(F(i)+F(ni1))F(n)=\sum^{n-1}_{i=0}{(F(i)+F(n-i-1))}

这不就是卡特兰数吗?

GCD&LCM

大家都知道gcd(x,y)gcd(x,y)=gcd(y,xmody)gcd(y,x\mod y),我们可以用如下的算法求gcdgcd

1
int gcd(int x,int y){return x%y==0?y:gcd(y,x%y);}

我们发现一个数对一个小于它的数取模后至少缩小一半,所以算法复杂度为log(n)log(n),实际可能还更小。

拓展欧几里得算法

ax+by=cax+by=c的正整数解。
首先根据裴蜀定理,我们知道:gcd(a,b)cgcd(a,b)|c,否则方程无解。
d=\\dfracc}{gcd(a,b)},则知道ax+by=gcd(a,b)ax+by=gcd(a,b)的解,我们把解出的x,yx,y都乘个dd就能得出原方程的解x,yx',y'

考虑把问题简单化,只求ax+by=gcd(a,b)ax+by=gcd(a,b)的正整数解。
考虑如下的方程:
ax1+by1=gcd(a,b)ax_1+by_1=gcd(a,b)
bx2+(amodb)y2=gcd(b,amodb)bx_2+(a\mod b)y_2=gcd(b,a\mod b)

我们知道gcd(a,b)=gcd(b,amodb)gcd(a,b)=gcd(b,a\mod b)
所以ax1+by1=bx2+(amodb)y2ax_1+by_1=bx_2+(a\mod b)y_2
又知道amodb=aa/b×ba\mod b=a-\lfloor a/b \rfloor \times b
我们把上面的柿子代入:
发现ax1+by1=bx2+(aa/b×b)y2ax_1+by_1=bx_2+(a-\lfloor a/b \rfloor \times b)y_2
化一下:ax1+by1=ay2+b(x2a/b×y2)ax_1+by_1=ay_2+b(x_2-\lfloor a/b \rfloor \times y_2)
对比两边系数,我们开心地发现x1=y2x_1=y_2y1=x2a/b×y2y_1=x_2-\lfloor a/b \rfloor \times y_2,于是我们可以根据x2,y2x_2,y_2算出x1,y1x_1,y_1

具体实现的时候,递归求解即可。

模板:

1
2
3
4
5
6
7
8
9
10
11
int gcd(int a,int b,int &d,int &x,int&y){
if(!b){
d=a,x=1,y=0;
return x;
}
else{
gcd(b,a%b,d,y,x);
y-=x*(a/b);
}
return x;
}

BSGS (北上广深算法)

已知a,b,pa,b,p,求ax=b(modp)a^x=b(\mod p)的正整数解。

考虑折半法,我们设B=PB=\sqrt{P},将xx化为B×ijB \times i-j的形式,其中i<B,j<Bi<B,j<B
xx代入原柿子,发现ax=aB×ij=aB×i/aja^x=a^{B \times i-j}=a^{B \times i} / a^j
把那个aja^j搞到右边,发现aB×i=b×aja^{B \times i}=b \times a^j
其中,左右两边只有i,ji,j两个变量,取值只有BB种可能。
考虑把b×ajb \times a^j预处理出来,丢进一个mapmap或哈希表里面。
后面一个一个枚举aB×ia^{B \times i},查询mapmap或哈希表里面有没有。

思维比较简单,但是代码比较长。
时间复杂度O(n)O(\sqrt {n})

模板题:P2485 [SDOI2011]计算器

题解:
对于询问1.1.,快速幂即可。
对于询问2.2.,逆元即可,注意判断不存在的情况。(当然extgcdextgcd也是可做的)
对于询问3.3.,需要运用BSGSBSGS算法,具体实现时需要注意B×ijB \times i-j可能为负,需要+p+p再取模,并且j=0j=0b×aj=bb \times a^j=b也是合法的。

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
75
76
77
78
79
80
81
82
83
84
85
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+(ch^'0');
ch=getchar();
}
return x*f;
}
int p;
inline int ksm(int b,int k){
int ans=1;
while (k){
if (k&1) ans=(ans*b)%p;
b=(b*b)%p;
k>>=1;
}
return ans;
}
inline void Solve1(int T){//快速幂
while (T--){
int y=read(),z=read();p=read();
y%=p;
printf("%lld\n",ksm(y,z));
}
}
inline void Solve2(int T){//逆元
while (T--){
int y=read(),z=read();p=read();
y%=p,z%=p;
if (y==0&&z!=0){
printf("Orz, I cannot find x!\n");
continue;
}
printf("%lld\n",ksm(y,p-2)*z%p);
}
}
map<int,int>M;
inline void BSGS(int a,int b){
if (a==0&&b!=0){
printf("Orz, I cannot find x!\n");
return ;
}
int B=(int)sqrt(p);
M.clear();
int now=b%p;
M[now]=0;
for (register int i=1;i<=B;++i){
now=(now*a)%p;
M[now]=i;
}
now=1;
int S=ksm(a,B);
for (register int j=1;j<=B;++j){
//判断a^{B \times i}是否在表中
now=(now*S)%p;
if (M.count(now)){
int ans=j*B-M[now];//M[now]=i
printf("%lld\n",(ans%p+p)%p);
return ;
}
}
printf("Orz, I cannot find x!\n");
}
inline void Solve3(int T){//BSGS
while (T--){
int y=read(),z=read();p=read();
BSGS(y%p,z);
}
}
#undef int
int main(){
#define int long long
int T=read(),K=read();
if (K==1) Solve1(T);
else if (K==2) Solve2(T);
else Solve3(T);
}

EXCRT 扩展中国剩余定理

在中国剩余定理的基础上,膜数p1,p2,p3...,pnp_1,p_2,p_3...,p_n可能不是质数:
假设现在我们已经求出前k1k-1个方程的解了,设为是xx
M=lcm(p1,p2,....pk1)M=lcm(p_{1},p_{2},....p_{k-1}),那么我们发现前k1k-1个方程的通解可以表示成x+M×albx+M \times alb(这些解都可以成立),其中albalb是整数。
我们要求tt,使得x+M×t=ak(modpk)x+M \times t=a_k (\mod p_k),也就是M×t=akx(modpk)M \times t =a_k-x(\mod p_k)
可以用extgcdextgcd求解tt,解完tt后,就发现前kk个式子的一个解为x+t×Mx+t \times M

整体思路也就是把式子合并合并再合并。

话说这个东西和CRTCRT关系好像不是很大。

高斯消元

可以求解类似于

a11×x+a12×y+a13×z.......=b1a_{11} \times x + a_{12} \times y + a_{13} \times z ....... = b_1

a21×x+a22×y+a23×z.......=b2a_{21} \times x + a_{22} \times y + a_{23} \times z ....... = b_2

a31×x+a32×y+a33×z.......=b3a_{31} \times x + a_{32} \times y + a_{33} \times z ....... = b_3

................................................................................

................................................................................

的方程。

假设我们有三个式子,假设albalb为现在未知数个数,现在albalb33

1×x+2×y+3×z=4...(1)1 \times x + 2 \times y + 3 \times z = 4 ... (1)

2×x+3×y+4×z=5...(2)2 \times x + 3 \times y + 4 \times z = 5 ... (2)

3×x+4×y+7×z=6...(3)3 \times x + 4 \times y + 7 \times z = 6 ... (3)

我们把(1)(1)中的xx设为主元,和(2)(3)(2)(3)式相减。

(2)(1)×2:1×y+2×z=3...(4)(2)-(1) \times 2 : -1 \times y + -2 \times z = -3 ... (4)

(3)(1)×3:2×y+2×z=6...(5)(3)-(1) \times 3 : -2 \times y + -2 \times z = -6 ... (5)

发现alb=2alb=2,继续执行一遍类似的操作,把(4)(4)中的yy设为主元,和(5)(5)式相减。

(5)(4)×2:2×z=0(5)-(4) \times 2 : 2 \times z = 0

我们就成功地解出了zz,把zz往上带,可以求出xxyy,就可以解出方程。
不仅对于alb=3alb=3的情况,albalb更大,也可以用类似的方法求出未知数。
时间复杂度O(alb2)O(alb^2)
高斯消元的操作可以转换为矩阵操作,其实就是把一个普通矩阵转换为单位矩阵。

逆矩阵

单位矩阵ee:只有主对角线为11的矩阵
容易发现单位矩阵乘任意矩阵,任意矩阵乘单位矩阵都为其本身
于是定义一个n×nn\times n的矩阵AA的逆矩阵为A1A^{-1}满足A×A1=eA \times A^{-1}=e;若B×A=CB \times A=C,则B=CA1B=C*A^{-1}
矩阵初等变换:交换两行/列,将一行/列的若干倍加到另一行/列上去(这些操作都可以通过左乘初等变换矩阵实现)
那么可以把AA搞成ee的变换矩阵就是A1A^{-1}
过程就是高斯消元,开始在原矩阵的旁边维护一个ee
对原矩阵的操作,都对这个矩阵也做一次
这样当AA变成ee了,原来的这个ee就变成A1A^{-1}

我们可以把操作矩阵定义为C1,C2,...CkC_1,C_2,...C_k,发现A×C1×C2×C3...×Ck=eA \times C_1 \times C_2 \times C_3 ... \times C_k=e
由于矩阵支持结合律,我们可以在这个式子两边同时乘以A1A^{-1},发现A×A1×C1×C2×C3...×Ck=e×A1A \times A^{-1} \times C_1 \times C_2 \times C_3 ... \times C_k=e \times A^{-1}
由于上面的两条性质A×A1=eA \times A^{-1}=eA×e=AA \times e = A,我们可以将式子化为e×C1×C2×C3...×Ck=A1e \times C_1 \times C_2 \times C_3 ... \times C_k = A^{-1}

所以原来的ee就变为A1A^{-1}了,是不是很巧妙?

拉格朗日插值法

全然わからない!,似乎是一个构造函数的神奇方法,留个坑待填。

一道小水题 P4986

评论