【算法】数学
【数论】数论——onion_cyc
【计数问题】计数问题(排列组合,容斥原理,卡特兰数)——onion_cyc
【概率与期望】链接
【链与反链】链接
【生成树计数(矩阵树定理)】专题链接
【快速幂】
原理:将指数化为二进制再分为若干个数相乘。
每次自己乘自己相当于平方,增加二进制权。
int quickpow(int x,int k) { int ans=1; while(k>0){ if(k&1)ans=1ll*ans*x%MOD; k>>=1; x=1ll*x*x%MOD; } return ans; }
【矩阵快速幂】
应用范围:1.图的矩阵。2.优化递推。
矩阵运算的意义在于把一切加法,乘法,乘幂的递推全部转化为独立的乘法,这样就可以用线段树或矩阵快速幂等维护。
特点:1.整体矩阵的规则变换。2.数据范围通常是10^18。
引用自:矩阵快速幂总结 by wust_wenhao
对于矩阵A和B,矩阵乘法运算定义为:
$$C(i,j)=\sum_{k=1}^{n}A(i,k)*B(k,j)$$
为了方便,这里只讨论行列相等的方阵。
更广泛的,可以对矩阵乘法重定义运算$\oplus$和$\otimes$,则有:
$$C(i,j)=\sum_{\oplus}(i,k) \otimes B(k,j)$$
注意,运算必须满足分配律,即:$a\otimes (b\oplus c)=(a\otimes b) \oplus (a \otimes c)$
矩阵乘法常用于优化常系数线性递推式,数据范围通常10^18,复杂度为O(N^3*log n),其中N为方阵边长。
矩阵乘法满足分配律,不满足交换律。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int n=2,MOD=10000; int a[n][n],b[n][n],t[n][n],m; void mul(int a[n][n],int b[n][n],int ans[n][n]) { for(int i=0;i<n;i++) for(int j=0;j<n;j++) { t[i][j]=0; for(int k=0;k<n;k++) t[i][j]=(t[i][j]+a[i][k]*b[k][j])%MOD; } for(int i=0;i<n;i++) for(int j=0;j<n;j++) ans[i][j]=t[i][j]; } int main() { scanf("%d",&m); while(m!=-1) { if(m==0){printf("0\n");scanf("%d",&m);continue;} m--; a[0][0]=a[0][1]=a[1][0]=1;a[1][1]=0; b[0][0]=1;b[1][0]=b[0][1]=b[1][1]=0; while(m>0) { if(m&1)mul(a,b,b); m>>=1; mul(a,a,a); } printf("%d\n",b[0][0]); scanf("%d",&m); } return 0; }
★矩阵乘法的第一种形式:图的矩阵(信息记载在整个矩阵上)
这类矩阵乘法通常定义$C^x$的意义(一般是走恰好x步),然后通过$C^{x-1}$递推得来。
例1:无向图i到j走x步的路径数
定义图的邻接矩阵$A$,走x步的答案矩阵$C^x$,通过枚举中转点k:
$$C^x(i,j)=\sum_{k=1}^{n}C^{x-1}(i,k)*A(k,j)$$
这恰好是矩阵乘法的形式,故$ans=A^x(i,j)$。
注意A(i,i)=0,否则就是<=x步。
初始单位矩阵只要考虑实际意义(走0步)即可得ans(i,i)=1。
例题:【BZOJ】1875: [SDOI2009]HH去散步 矩阵快速幂
例2:无向图i到j走x步的最短路
定义图的邻接边权矩阵$A$,无边为inf,通过枚举中转点k:
$$C^x(i,j)=\min_k\{C^{x-1}(i,k)+A(k,j)\}$$
重定义运算,容易发现满足分配律(a+min{b,c}=min{a+b,a+c}),故$ans=A^x(i,j)$。
A(i,i)=inf,ans(i,i)=0。
例题:【BZOJ】1706: [usaco2007 Nov]relays 奶牛接力跑
【BZOJ】1297: [SCOI2009]迷路 拆点后是上题
【CodeForces】576 D. Flights for Regular Customers 倍增
★第二类矩阵乘法:优化常系数递推方程(信息记载在列向量上)
通法:构造列向量A和转移矩阵T,使得$T \times A^n=A^{n+1}$,从而有$T^n \times A^0=A^n$,然后就可以对T^n进行矩阵快速幂。
或者,$T^k \times A^n=A^{n+k}$
列向量中可能需要一些没有出现的未知数方便构造转移矩阵,目的是要计算出f[i]的同时将其它项转移成为A[i]。
eg. f[i]=f[i-1]+f[i-2]+f[i-4]
(我是图>w<)
为什么多个f[i-3]?因为f[i-4]->f[i-3]的转移需要f[i-3]的存在。
【POJ】3233 Matrix Power Series 经典题:二分序列求递增幂。
套路:
1.加法的常数进列向量,乘法的常数进转移矩阵,n在幂把底数的幂进列向量,转移乘底数
4.矩阵和式化递推
5.★非递推式矩乘:少量固定的元素,固定的变化规则(不存在决策),一次变化是一次转移,求最终状态。
6.需要使用同列元素更新时,考虑同列元素的更新来源,从上一列拿。
7.二维DP数组可以直接将第二维视为列向量,二维常数数组直接作为转移矩阵的参考。
8.二项式可以将展开项放进列向量方便转移。(hdu)
《bzoj4417》矩乘只能优化从前一两项递推过来的,所以可以通过记录前缀和等强行化成递推式,矩乘优化
《bzoj1898》转移矩阵的变化以12为周期,那么将12个不同的转移矩阵处理相乘,就可以快速幂计算k/12了,然后暴力计算k%12就可以了。矩乘变化非常灵活。
《bzoj4870》题解,居然是考虑答案的实际组合意义是%k=r(因为k和r都很小),然后f[i][j]表示前i件物品取后%k=j的方案数……然后随便递推矩乘就可以了QAQ。
《bzoj5118》求Fib(2^n),n<=10^5,%大素数。
由欧拉定理,$A^{2^n-1} \ \ mod \ \ p =A^{2^n-1\ \ mod \ \ p-1} \ \ mod \ \ p$,然后就没了……
《bzoj2004》【BZOJ】2004: [Hnoi2010]Bus 公交线路 动态规划+矩阵快速幂
状态压缩,将状态作为元素矩乘。
【高斯消元】gauss
标准过程:
1.一个n行n列的系数方程组,n+1列为答案。
2.对于第i行,第i列为主元,找到同列最大主元并交换。【这里记得比较绝对值!!!】
3.用第i行减倍消去下面行的第i列元。
4.回代:对于第i行,非主元直接取值减去,除以主元系数得到i元存在a[i][n+1]。
void gauss() { int r; for(int i=1;i<=n;i++) { r=i; for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>fabs(a[r][i]))r=j; if(r!=i)for(int j=1;j<=n+1;j++)swap(a[r][j],a[i][j]); for(int k=i+1;k<=n;k++) for(int j=n+1;j>=i;j--) a[k][j]-=a[k][i]/a[i][i]*a[i][j]; } for(int i=n;i>=1;i--) { for(int j=i+1;j<=n;j++) a[i][n+1]-=a[j][n+1]*a[i][j]; a[i][n+1]/=a[i][i]; } }
自由元问题:
对于第i列,如果从i~n行找不到一个系数不为0的a[j][i]来当主元素,说明该列为自由元。
自由元的选择不一定,但自由元的个数一定。
有自由元的话回代没有意义,应该在确定自由元后再回代。
方程无解或有无限解问题:
(那些左边全0的方程就不能再贡献非0系数了,这些方程要么恒成立要么矛盾。)
重要类型——异或方程组:
选择主元只须1。
消元只消去系数为1的行,整行对应异或。
主元系数为1,回代。
void gauss()//保证有解 { int r; for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++)if(a[j][i]){r=j;break;} if(r!=i)for(int j=1;j<=n+1;j++)swap(a[i][j],a[r][j]); for(int j=i+1;j<=n;j++)if(a[j][i]) for(int k=i;k<=n+1;k++) a[j][k]^=a[i][k]; } for(int i=n;i>=1;i--) for(int j=i+1;j<=n;j++) if(a[i][j])a[i][n+1]^=a[j][n+1]; }
常与期望概率题目结合,列出n个式子(循环调用)后解方程。
【异或】异或的性质及运用
1.异或就是不进位加法(当然是对于某一位来说)。
2.满足结合律,而且可以拿到等式右边,不变号。
3.满足自反性,即A^A=0,可以用于消去元素。
找多余数:利用自反性,全部异或和-异或和。
相同的数字,奇数个异或得原数,偶数个异或得0。
4.与0异或不变,与1异或取反。
【线性基】解决相互异或的值域问题
参考:[学习笔记]线性基 byYveh
构造:对数字集合a[i]构造线性基数组b[i],每个数字从高位到低位枚举。假设当前为第x位,如果b[x]存在数字那么异或b[x],否则将当前值赋给b[x]。(b[x]存的是线性基中最高位的1在第x位的数字)
for(int i=1;i<=n;i++) { for(int j=62;j>=0;j--)if(a[i]&(1<<j)) { if(!b[j]){b[j]=a[i];break;} else a[i]^=b[j]; } }
特点:
1.线性基中的数字2^m种组合的异或和的值域,和原数字集合互相异或的非零值域一致。(原数字集合值域可能存在0)
2.线性基中的数字互相异或的值不为0。
3.大小为m的线性基在数字集合中有m个依赖数字(构造时赋值的数字),这m个数字可以直接构成线性基(因为相互异或是等价的)。
故线性基是原数字集合中,最大的值域不含0的子集。
应用:
1.判断异或为0子序列的存在:若有数字无须加入线性基则根据自反性存在。
使序列不存在子序列异或和为0:去除除了线性基依赖数字外的其他数字,则构造不出0。
2.最大异或值:从高位到低位扫描线性基,对答案有贡献则异或。
3.最小异或值:即最低位线性基
4.第k小值:首先定义“关键位”为线性基中m个数字的最高位,改造线性基使得每个数字的所有关键位只有最高位是1。
具体方法只要枚举每个数字的每个关键位,如果是1就异或对应的值。
这样之后,当最终数字的关键位的01排列确定后,数字也就唯一确定了(因为选择的组合唯一确定)。
那么第k小值就是 [ k的二进制对应的01组合 ] 作为 [ 线性基数字的选择 ] 得到的数字。
例题:
1.【BZOJ】3105: [cqoi2013]新Nim游戏和[bzoj 2460]线性基+贪心+证明过程
给定序列,求最大权 [ 值域不含0 ] 的子集。
贪心,从大到小排序后加入线性基。这样是对的……证明见第二个链接。
2.
【博弈论】
引用自:博弈问题及SG函数
博弈相关论文(只是贴一贴):张一飞博弈 浅谈SG游戏的若干拓展及变形 解析一类组合游戏
<Nim>
N-position 先手必胜局面
P-position 先手必败局面
1.无法进行任何移动的局面(terminal position)是P-position。
2.可以移动到P-position的局面是N-position。 先手必胜,采用正确的决策就能到达先手必败。
3.所有移动都导致N-position的局面是P-position。 先手必败,无论怎么走都只能到达先手必胜。
特别地,(0,0)是P-position,(x,x)是P-position(每当你拿走一部分,对手拿走相同的一部分)。
结论:对于一个Nim游戏的局面(a1,a2,...,an),它是P-position当且仅当a1^a2^...^an=0。
证明:
1.无法进行任何移动的局面(terminal position)是P-position。
显然,因为0^0^...^0=0。
2.可以移动到P-position的局面是N-position。
对于局面a1^a2^...^an=k,一定存在某个ai,它的二进制表示在k的最高位上是1,这时ai^k<ai一定成立。则我们可以通过移动将ai改变成ai'=ai^k,此时 a1^a2^...^ai'^...^an=0。
更广泛的,每个ai--->ai^k(ai^k<ai)都可以实现N--->P。
3.所有移动都导致N-position的局面是P-position。
显然,ai--->ai是不合法的移动,所以移动后右项必定非0。
<SG>
定义DAG游戏,一方每次只能使一颗棋子走一条边,不可走则败。
定义mex{...}表示最小的不属于这个集合的非负整数。
对于每个顶点,SG函数:g(x)=mex{ g(y) | y是x的后继 }
★SG函数值中,0对应P-position(先手必败),!0对应N-position(先手必胜)。
1.无法进行任何移动的局面(terminal position)是P-position,g(x)=0。
2.可以移动到P-position的局面是N-position。N-position对应g(x)!=0,其后继必有至少一个0。
3.所有移动都导致N-position的局面是P-position。P-position对应g(x)=0,其后继必然没有0。
★性质:
1.对于任意的局面,如果它的SG值为0,那么它的任何一个后继局面的SG值不为0。
2.对于任意的局面,如果它的SG值不为0,那么它一定有一个后继局面的SG值为0。
结论:当前局面是P-position当且仅当所有棋子所在的位置的SG函数的异或为0。
进一步地,★总局面的SG值是子局面SG值的异或和。
Nim游戏中,石子数恰好对应g函数。
考虑游戏的和时,可以将每一个单一游戏视为SG值数目的一堆石子,游戏的和就等价于Nim游戏。
对于一个可以用SG函数表达的组合游戏,其局面要么先手必胜要么先手必败。
解法:将复杂的游戏分成若干个子游戏,对于每个子游戏找出它的SG函数,然后全部异或起来就得到了原游戏的SG函数,就可以解决原游戏了。
前面都是理论,真正实践起来必须熟知SG函数的性质和运用QAQ
感谢SG函数模板 by jumping_frog
这篇博客让我恍然大悟……原来SG函数是这个样子。
计算从1-n范围内的SG值。
f[]存储可以走的步数,需要从小到大排序
1.可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);
2.可选步数为任意步,SG(x) = x;
3.可选步数为一系列不连续的数,用GetSG()计算
SG[a]=mex(SG[b]) a-x=b.
SG函数套路:分成互不影响的子游戏,考虑子游戏中的转移,得到子游戏的SG值,得到总游戏的SG异或和。
SG值可以DFS,也可以预处理。
<阶梯博弈>
引用自:阶梯博弈(没怎么搞懂) by ~生如夏花
题意:博弈在一列阶梯上进行,每个阶梯上放着自然数个点,两个人进行阶梯博弈,每一步可以将一个集体上的若干个点( >=1 )移到前面去,最后没有点可以移动的人输。
转化:从最底开始标号123,将每个奇数台阶的石子视为一堆,则问题可以视为所有奇数阶梯的石子在做Nim博弈。
说明:如果对手移动偶数堆到奇数堆,我们可以立即从那个奇数堆移动相同数量的石子到偶数堆,反之亦然。
如果对手移动奇数堆,则我们也移动奇数堆,则转化为奇数堆Nim游戏,反之亦然。
<二分图博弈>
引用自:二分图博弈
题意:状态图(点)为二分图(X集和Y集),任意合法的决策(边)是状态从一类跳转到另一类,不可重复访问点,有起点S,不可再移动视为终点T,到达后胜利。
题解:
不妨设起点在二分图的X集中,那么先手只能从X集移动到Y集,后手只能从Y集移动到X集。若最后停留在X集且无法移动则先手负,停留在Y集则后手负。
考虑该二分图的某个最大匹配。
若起点S∈X不属于该最大匹配,则先手所转移到的点y∈Y一定属于最大匹配(否则s-y是一个匹配,与最大匹配矛盾),后手沿着最大匹配的边走即可。终点T一定不可能在Y集中(否则,若t在Y集中,s-...-t为一增广路,与最大匹配矛盾)。因此起点S不属于某个最大匹配时,T在同侧,先手必败,后手必胜。
若起点s∈X属于该最大匹配。则将s从图中删除,再求图的最大匹配。若最大匹配数不变,则s还是不属于某最大匹配,先手必败。否则该图的任意最大匹配都包含s,则先手沿着最大匹配的边走即可,根据上面的分析,起点S属于所有最大匹配时,T在异侧,先手必胜。
【01分数规划】
引用自:[Algorithm]01分数规划——Update:2012年7月27日 by PerSeAwe
有n个元素可被选择,ai表示选择的收益,bi表示选择的代价,ans=Σ((ai*xi)/(bi*xi))。
令f(ans)=Σ(a[i]*x[i])-ans*Σ(b[i]*x[i])(特别注意是减去代价),简单变型后得f(ans)=Σ(ai-ans*bi)*xi。
令di=ai-ans*bi,则f(ans)=Σdi*xi。
二分ans,当存在方案使f(ans)>=0时,变换回去得到ans<=Σ((ai*xi)/(bi*xi)),也就是可以得到更大的ans。
所有方案都使f(ans)<0时,变换回去得到ans>Σ((ai*xi)/(bi*xi)),也就是不存在方案满足当前ans,ans应该更小。
随着ans的增长,d数组单调递减,也就是必然存在一个最大的ans使得存在方案f(ans)>0。
★<最优比率环>【BZOJ】1690: [Usaco2007 Dec]奶牛的旅行
【置换】
置换就是把n个元素做一个全排列,例如(a1,a2,a3)->(b1,b2,b3)的一一映射就是置换,这n个元素的一个全排列成为一种方案。
置换之间可以定义乘法,置换A*B就是先做A再做B后的结果,满足结合律,不满足交换律。
置换可以分解成循环的乘积,并且每个循环节内部依次做len-1次对换就完成了置换。
(分解的正确性:每个元素有要去的位置,即后继,每个元素原位置有要来的元素,即前驱,于是形成若干个环,每个环都是循环)
定义循环节为该置换循环分解中循环的个数。
等价类计数问题:题目中会定义等价关系,目标是统计等价类的个数(本质不同的元素个数)。
首先用一个置换群来描述等价关系,如果群中的置换可以将一个方案映射到另一个方案,就说二者等价。
根据群的封闭性定义,F中任意两个置换的乘积也应当在F中。
对于置换f,若一个方案s经过置换后不变,称s为f的不动点,将f的不动点数目记为C(f)。
根据Burnside引理,等价类数目为所有C(f)的平均值。(f∈F)
考虑经典的方格黑白染色问题,统计C(f)的方式:每个循环内部必须同色,那么C(f)=2^num(num为置换f的循环节)。
假设有k种颜色,则C(f)=k^num,从而得到Polya定理。
扩展:组合数学——polya
【其它】
1.错排公式:设D(n)表示n个数错排的方案数(不在原位置)。
D(n)=(n-1)*(D(n-1)+D(n-2))——第一个数有n-1个放置位置,对于位置k如果放在第1位则D(n-2),否则D(n-1)。
特别地,D(1)=0,D(2)=1。(计数问题——考虑状态减少的转移,最后一个数字的安排)
通项求解:
(1)令D(n)=n!*N(n),容易推到N(n)-N(n-1)=-1/n(N(n-1)-N(n-2),特别地,N(2)-N(1)=1/2。
将右边不断带入,可以得到N(n)-N(n-1)=(-1)^n/n!,再运用累加法可得D(n)=n!*(1/2!-1/3!+1/4!-...+(-1)^n/n!)。
(2)运用容斥原理,已知集合并的补集(至少i个数不错排),求解集合交(n个数错排)
+至少0个n!,-至少1个n*(n-1)!,+至少2个C(n,2)*(n-2)!……得到:D(n)=n!-n!/1!-n!/2!...即D(n)=n!*(1/2!-1/3!+1/4!-...+(-1)^n/n!)。
(3)
例题:求正确位置数字>=n/2的排列数,转化为<n/2的错排数,组合数枚举错排数字然后运用错排公式。
2.平方和公式:∑k^2=n(n+1)(2n+1)/6
3.快速排序:利用分治法,每次找基准数,从右找大,从左找小,找到后同时交换,哨兵相撞后完成,递归。最优O(n log n),最坏O(n^2),平均O(n log n)。
4.后缀表达式:设置数字栈+符号栈,加入符号时弹出优先度高的符号,弹出符号栈时数字栈栈顶与次顶结合后栈顶-1。
5.均值不等式:2/(1/a+1/b)<=√ab<=(a+b)/2<=√[(a^2+b^2)/2]
大步小步法
原根和指标
算组合数用公式+乘法逆元更快
积性函数前缀和(杜教筛)
生成函数
多项式开平方 多项式快速幂
【矩阵】【置换】
【快速傅里叶变换FFT】【NTT】【快速沃尔什变换】【莫比乌斯反演】【构造函数/生成函数】【分数规划】【线性规划】【单纯形】