【算法】数学

【数论】数论——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;
}
View Code

 

【矩阵快速幂】

应用范围:1.图的矩阵。2.优化递推。

矩阵运算的意义在于把一切加法,乘法,乘幂的递推全部转化为独立的乘法,这样就可以用线段树或矩阵快速幂等维护。

特点:1.整体矩阵的规则变换。2.数据范围通常是10^18。

引用自:矩阵快速幂总结 by wust_wenhao

矩阵十题(3) by jumping_frog

对于矩阵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;
}
View Code

例题:【POJ】3070 Fibonacci

★矩阵乘法的第一种形式:图的矩阵(信息记载在整个矩阵上)

这类矩阵乘法通常定义$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]迷路 拆点后是上题

【BZOJ】2165: 大楼 倍增

【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]的存在。

例题:【bzoj】2326 [HNOI2011]数学作业

【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];
     }
}
gauss

自由元问题

对于第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];
    }
}
View Code

特点

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

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]

 

大步小步法

原根和指标

N=8时非递归FFT的演示

算组合数用公式+乘法逆元更快

积性函数前缀和(杜教筛)

生成函数

多项式开平方 多项式快速幂

矩阵】【置换】

【快速傅里叶变换FFT】【NTT】【快速沃尔什变换】【莫比乌斯反演】【构造函数/生成函数】【分数规划】【线性规划】【单纯形】

 

posted @ 2016-12-08 19:25  ONION_CYC  阅读(1076)  评论(0编辑  收藏  举报