高数基础补档

复数相关

棣莫弗定理:.

欧拉公式:.

也就是,同时又有,证明直接使用泰勒展开.

单位根:对于,我们会有个根,设第个根为.那么我们有:.

单位根有以下性质:

  1. 折半引理:,由我们上面推导的通项公式即可证明.

  2. 消去引理:,同样使用通项,运用三角恒等变换可证明.

分圆多项式

上复平面,设,也就是是方程的复根.我们把这个方程写开:

不难发现是平凡解.

我们不妨定义,事实上我们有:

事实上,我们令,容易发现,又容易发现.

接下来观察,带入得到.

接下来考虑拿到,只需求.

Example(尺规做正n边形问题)

碰瓷高斯问题.

一步一步来.根据尺规作图理论:尺规作图只可以实现五种操作.而对于正边形,显然只要我们能将用只含上述五种操作和若干整数表示出来,那就一定可行.

正五边形问题

观察正五边形在复平面上的图像,注意到有两对点互为共轭复数,我们令:

不难验证:

可以求出复合条件的解,将带入又有:

于是我们显然可以求得.

正七边形

类似正五边形,最后会导出三次方程:根中含有三次根号,因此不行.

正n边形

要解决正17边形,只需要解决正n边形,然后令n=17即可.

你问我咋想到的下面的证明?问高斯去.

下面其实用到了ntt知识,但我懒得扔下面了.

先假设,我们用代替.

我们有,由于其它的都可以表示成它的幂,因此我们记.

我们现在想要这么分组:

泰勒展开

.的时候是麦克劳林级数.

麦克劳林展开是生成函数的基础,我们所谓的生成函数的封闭形式其实就是麦克劳林展开的逆运算(可能也不能完全等价,但笔者能力不够,暂且这么理解).

离散傅里叶变换

考虑如何将一个交换群以一个函数(未必是同态)映射到复数,则这个东西天然对形成线性空间且维度为.

结构定理首先给出,考虑映射,其固定一个,而将一个映射到.

现在考虑这个映射本身所组成的交换群,容易发现以下性质:

  1. .
  2. .
  3. .
  4. .

考虑在上面的复内积.这显然是一组正交基.从而任何一个都可以写作的形式,而且,容易见到这里,从而其实也是一个的函数.那如果拿它来做内积会怎么样呢?请看: 于是我们见到下面两个过程:

  1. .
  2. .

接下来定义卷积.

还是应该再思考一下两者的地位,见到:

  1. .
  2. .
  3. .
  4. .

考虑(1),显然.

(2)(3)显然.

对于(4):

对于(5):

多项式

多项式基础

点值表示法和系数表示法

代数基本定理:一个次方程在复数域上有且只有个根.

定理:一个次多项式在个不同点的取值唯一确定了该多项式.

证明:考虑反证法,假设命题不成立,则存在两个次多项式且有.

,那么至多是一个次多项式且,也就是个根,与代数基本定理不符合.

由上面的内容,多项式有点值表示法和系数表示法两种:

系数表示法:.

点值表示法:.

已知多项式点值表示法求系数表示法的过程被称为插值.

拉格朗日插值

构造多项式.显然当时,该多项式的答案为.

另外,如果,不难发现这个式子可以写成:

多项式运算

考虑两个多项式相乘,如果我们已知他们的点值表示法,显然可以直接相乘.

这为我们提供了一种思路:先将系数表示法转化为点值表示法,进行相乘之后再转化回系数表示法.

这引出以FFT为代表的多项式乘法,并拓展到了多种多项式运算.

多项式乘法

快速傅里叶变换(FFT)
DFT

次单位根(默认是二的整次幂,如果少了的话补零,设)分别带入得到点值向量.

如果朴素带入,复杂度显然不可接受.

考虑:

A_2(x)=\sum_{i=2k+1,k\in\mathbb{N}}{n-1}a_ix{k}\A(x)=A_1(x2)+xA_2(x2)\

接下来分类讨论:

,我们有:

根据折半引理:

这样我们处理完了前半部分.

,我们有:

根据消去引理:

综上,我们可以递归处理,然后合并得到的答案,可以分治.

IDFT

,构造多项式.

我们求出的点值表示,设,也即:

时,显然.

否则根据等比数列求和公式,.

所以.

那么我们有

a_k=\frac{c_k}{n}\

写法

递归写法显然.

递归过程中,第层相当于在根据数在第位的二进制数是还是来分类.那显然可以求出最后一层的数组,然后向上合并.

(没找到fft的代码,懒得写了,直接用的ntt的,注意快速幂要处理幂为负数的情况).

inline void init_rev(int limit,int k){
    rev[0]=0;
    for(int i=1;i<limit;++i){
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
    }
    return ;
}
inline void ntt(int limit,ll *a,ll t){//DFT:t=1;IDFT:t=-1
    for(int i=0;i<limit;++i){
        if(i<rev[i])std::swap(a[i],a[rev[i]]);
    }
    for(int mid=1;mid<limit;mid=mid<<1){
        int n=mid<<1;
        ll wn=mpow(gn,t*(mod-1)/n);
        for(int i=0;i<limit;i+=n){
            ll w=1;
            for(int k=0;k<mid;++k,w=w*wn%mod){
                ll wakn=w*a[i+k+mid]%mod;
                ll ak=a[i+k];
                a[i+k]=ak+wakn;Mod(a[i+k]);
                a[i+k+mid]=ak-wakn+mod;Mod(a[i+k+mid]);
            }
        }
    }
    if(t==-1){
        ll inv=mpow(limit,-1);
        for(int i=0;i<limit;++i){
            a[i]=a[i]*inv%mod;
        }
    }
    return ;
}
快速数论变换(NTT)

由于FFT中的单位根会产生精度误差,因此在膜意义下,通常会选择NTT来进行多项式乘法.

NTT与FFT的运算过程基本相同,证明过程基本相同,唯一不同的是将单位根改为了原根.

根据上面FFT的证明过程,我们知道,设原根为,,只需要证明原根满足以下条件,就可以进行变换:

  1. ,证明由原根的性质.

  2. 折半引理:,证明显然.

  3. 消去引理:.由于,该结论显然成立.

由上我们证明了,我们完全可以使用代替进行变换.

另外,注意到,而.因而,当的时候,可以直接求出.这也是为什么大部分NTT题目都使用作为模数的原因.

范德蒙德矩阵理解

范德蒙德矩阵形如:

如果取单位根,我们有:

这就是我们在做FFT(一个线性变换)的时候的变换矩阵.所以我们有:

分治FFT

给定,求,答案对取膜.

考虑分治,假如我们已经知道了.那我们可以计算出这段部分对的贡献.

这显然是一个卷积的形式,我们直接计算的乘积并贡献上去.

多项式求逆

对于多项式,找到使得.显然是唯一的.

首先不妨设.

如果我们已知,同时肯定有,相减得到.

两边平方:

两边乘一下:

根据主定理,这么做复杂度是的.

同时,多项式求逆可以解决上面提到的分治FFT.我们注意到分治FFT的条件等价于:

于是可以直接做多项式求逆.

多项式除法

对于次多项式次多项式,找到使得.

考虑对于次多项式,令,如果设为其项前的系数,不难发现.

那么我们有:

于是只要做一遍多项式求逆即可求得,再做一遍相减既可以得到.

多项式ln

给出次多项式,求一个多项式,满足.

我们有:

另外,考虑中间求导的过程中,其实模数也要相应发生变化,但是由于模数是从更高次变低,而最后积分的时候又要变回来,所以可以直接忽略变化.

定理:在模意义下当且仅当的时候,有对数多项式.

我们对最后再做一步:

首先,如果,则,因此不能放到模意义下,自然不存在对数多项式.

的时候,,因此可以直接求出答案.

牛顿迭代

给定多项式,求一个多项式满足.

首先的时候,也就是求.这个要根据具体题目具体分析求出.

假设我们已经求出了在意义下的答案,我们考虑在处做泰勒展开:

考虑,由于,因此,因此.

于是我们有:

牛顿迭代可以用来证明多项式求逆的式子同样正确.

多项式开方

给定,设,求零点.

根据牛顿迭代,有:

还没完,用牛顿迭代前一定要求的解,也就是的开根,用二次剩余算.

多项式exp

给定,设,求零点.

根据牛顿迭代,有:

还没完,还需要求的解,注意到存在当且仅当,此时.

多项式快速幂

后求即可,唯一的问题是为什么指数可以对取膜.

我们有一个结论:

这个结论很简单,注意到即可.

而又由于,因此,通常取,于是就可以直接对取膜.

多项式运算全家桶(重载运算符版)

我们必须指出的一点是,虽然重载运算符很好看,但是大部分情况下还是需要指针传参.例如在这里,由于做的时候的直接数组传参,会导致的复杂度退化到.

#include<algorithm>
#include<cstring>
#include<cstdio>
#define ll long long
#define qwq 300007
const int mod=998244353;
const int gn=3;
int n;
int rev[qwq];
ll inv[qwq];
struct poly{
    ll x[qwq];
    int limit,k;
}a;
inline void Mod(ll &x){
    if(x>=mod)x-=mod;
    if(x<0)x+=mod;
    return ;
}
inline ll mpow(ll x,ll k){
    if(k<0)k+=mod-1;
    ll ans=1;
    for(;k;k=k>>1,x=x*x%mod){
        if(k&1)ans=ans*x%mod;
    }
    return ans;
}
inline ll get_inv(int x){
    if(x>qwq-7)return mpow(x,mod-2);
    if(inv[x])return inv[x];
    else return inv[x]=mpow(x,mod-2);
}
inline void init_rev(int limit,int k){
    for(int i=1;i<limit;++i){
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
    }
    return ;
}
inline void ntt(poly *a,int t){
    init_rev((a->limit),(a->k));
    for(int i=0;i<(a->limit);++i){
        if(rev[i]>i)std::swap(a->x[i],a->x[rev[i]]);
    }
    for(int mid=1;mid<(a->limit);mid=mid<<1){
        int n=mid<<1;
        ll wn=mpow(gn,t*(mod-1)/n);
        for(int i=0;i<(a->limit);i+=n){
            ll w=1;
            for(int k=0;k<mid;++k,w=w*wn%mod){
                ll wakn=w*(a->x[i+k+mid])%mod;
                ll ak=(a->x[i+k]);
                a->x[i+k]=ak+wakn;Mod(a->x[i+k]);
                a->x[i+k+mid]=ak-wakn;Mod(a->x[i+k+mid]);
            }
        }
    }
    if(t==-1){
        ll inv=get_inv(a->limit);
        for(int i=0;i<a->limit;++i){
            a->x[i]=(a->x[i])*inv%mod;
        }
    }
    return ;
}
inline poly operator %(poly x,int k){//对x^{2^k}取膜
    for(int i=(1<<k);i<x.limit;++i)x.x[i]=0;
    x.k=k;
    x.limit=1<<k;
    return x;
}
inline poly operator +(poly x,poly y){
    x.limit=std::max(x.limit,y.limit);
    x.k=std::max(x.k,y.k);
    for(int i=0;i<x.limit;++i){
        x.x[i]+=y.x[i];Mod(x.x[i]);
    }
    return x;
}
inline poly operator -(poly x,poly y){
    x.limit=std::max(x.limit,y.limit);
    x.k=std::max(x.k,y.k);
    for(int i=0;i<x.limit;++i){
        x.x[i]-=y.x[i];Mod(x.x[i]);
    }
    return x;
}
inline poly operator*(ll x,poly y){
    for(int i=0;i<y.limit;++i){
        y.x[i]=x*y.x[i]%mod;
    }
    return y;
}
inline poly operator *(poly x,poly y){
    x.limit=std::max(x.limit,y.limit)<<1;
    x.k=std::max(x.k,y.k)+1;
    y.limit=x.limit;y.k=x.k;
    ntt(&x,1);ntt(&y,1);
    for(int i=0;i<x.limit;++i){
        x.x[i]=x.x[i]*y.x[i]%mod;
    }
    ntt(&x,-1);
    return x;
}
poly q_inv,tmp_inv;
inline poly invpoly(poly x){
    for(int i=0;i<(x.limit<<1);++i){
        q_inv.x[i]=tmp_inv.x[i]=0;
    }
    q_inv.x[0]=mpow(x.x[0],-1);q_inv.limit=1,q_inv.k=0;
    for(int lim=2,k=1;lim<=x.limit;lim=lim<<1,++k){
        for(int i=0;i<lim;++i){
            tmp_inv.x[i]=x.x[i];
        }
        tmp_inv.limit=q_inv.limit=lim;
        tmp_inv.k=q_inv.k=k;
        q_inv=2ll*q_inv-q_inv*q_inv%k*tmp_inv%k;
    }
    q_inv.limit=x.limit;q_inv.k=x.k;
    return q_inv;
}
inline poly operator /(poly x,poly y){
    int lim=x.limit,k=x.k;
    x=x*invpoly(y);
    for(int i=lim;i<x.limit;++i){
        x.x[i]=0;
    }
    x.limit=lim,x.k=k;
    return x;
}
inline poly Dpoly(poly x){//求导
    for(int i=1;i<x.limit;++i){
        x.x[i-1]=x.x[i]*i%mod;
        x.x[i]=0;
    }
    return x;
}
inline poly Spoly(poly x){//积分
    for(int i=x.limit-1;i>=0;--i){
        x.x[i+1]=x.x[i]*get_inv(i+1)%mod;
        x.x[i]=0;
    }
    return x;
}
inline poly lnpoly(poly x){
    if(x.x[0]!=1);//无解
    return Spoly(Dpoly(x)/x);
}
poly q_exp,tmp_exp;
inline poly exppoly(poly x){
    if(x.x[0]!=0);//无解
    for(int i=0;i<(x.limit<<1);++i){
        q_exp.x[i]=tmp_exp.x[i]=0;
    }
    q_exp.x[0]=1;
    for(int lim=2,k=1;lim<=x.limit;lim=lim<<1,++k){
        for(int i=0;i<lim;++i)tmp_exp.x[i]=x.x[i];
        tmp_exp.limit=q_exp.limit=lim;
        tmp_exp.k=q_exp.k=k;
        q_exp=(q_exp+q_exp*(tmp_exp-lnpoly(q_exp)))%k;
    }
    return q_exp;
}
poly q_sqrt,tmp_sqrt;
inline poly sqrtpoly(poly x){
    if(x.x[0]!=1);//如果不是1要做二次剩余
    q_sqrt.x[0]=1;
    for(int lim=2,k=1;lim<=x.limit;lim=lim<<1,++k){
        for(int i=0;i<lim;++i)tmp_sqrt.x[i]=x.x[i];
        tmp_sqrt.limit=q_sqrt.limit=lim;
        tmp_sqrt.k=q_sqrt.k=k;
        q_sqrt=(q_sqrt*q_sqrt%k+tmp_sqrt)/(2ll*q_sqrt)%k;
    }
    return q_sqrt;
}
inline poly powpoly(poly x,ll k){
    if(x.x[0]!=1);//无解
    x=k*lnpoly(x);
    return exppoly(x);
}

集合幂级数

集合幂级数形如,其中二进制数表示的一个子集,用表示该子集大小,等价于对二进制使用的popcount函数.

下述级数如无特别说明均为集合幂级数.

与/或卷积

高维前缀和:.

高维后缀和:.

上述过程又称快速莫比乌斯变换(FMT).

for(int i=0;i<n;i++)
    for(int j=0;j<(1<<n);j++)
        if(j&(1<<i)) a[j]+=a[j^(1<<i)];//高维前缀和
for(int i=0;i<n;i++)
    for(int j=0;j<(1<<n);j++)
        if(j&(1<<i)) a[j^(1<<i)]+=a[j];//高维后缀和

或卷积:.

与卷积:.

二者求法类似,考虑如何求的或卷积:

引理:

,则,逆命题同样成立.

,则,逆命题同样成立.

的高维前缀和分别为,我们有:

现在考虑已知,本质上是一个反演.注意到,我们有:

于是有(实际上就是个差分的过程).

因而做两遍高维前缀和再反推回去即可,复杂度.

与卷积即改为高维后缀和.

inline void FWT_and(ll *a,ll t,int limit){//FWT:t=1;IFWT:t=-1
    for(int mid=1;mid<limit;mid=mid<<1){
        int n=mid<<1;
        for(int j=0;j<limit;j+=n){
            for(int k=0;k<mid;++k){
                a[j+k]+=t*a[j+k+mid]%mod;
                Mod(a[j+k]);
            }
        }
    }
    return ;
}

inline void FWT_or(ll *a,ll t,int limit){//FWT:t=1;IFWT:t=-1
    for(int mid=1;mid<limit;mid=mid<<1){
        int n=mid<<1;
        for(int j=0;j<limit;j+=n){
            for(int k=0;k<mid;++k){
                a[j+k+mid]+=t*a[j+k]%mod;
                Mod(a[j+k+mid]);
            }
        }
    }
    return ;
}

异或卷积

异或卷积:.

引理:.

证明的话考虑如果,二者显然相等;当的时候,左右都等价于.

快速沃尔什变换(FWT):

定义集合幂级数,满足.

那么有:

时间复杂度.

逆运算的话考虑实现过程,反向就行.不过可以把过程中乘上的都提出来乘到最后.

FMT可以看作是FWT在解决与/或卷积时的特例.

inline void FWT_xor(ll *a,ll t,int limit){//FWT:t=1;IFWT:t=-1
    for(int mid=1;mid<limit;mid=mid<<1){
        int n=mid<<1;
        for(int j=0;j<limit;j+=n){
            for(int k=0;k<mid;++k){
                ll x=a[j+k],y=a[j+k+mid];
                a[j+k]=x+y;Mod(a[j+k]);
                a[j+k+mid]=x-y;Mod(a[j+k+mid]);
            }
        }
    }
    if(t==-1){
        ll inv=mpow(limit,mod-2);
        for(int i=0;i<limit;++i)a[i]=a[i]*inv%mod;
    }
    return ;
}

快速沃尔什变换

线性代数角度

我们来重定义一下所谓的FWT.

首先类比FFT,我们希望存在一个线性变换,使得

  1. ,则.

  2. 这个线性变换是可逆的.

  3. 做这个线性变换和其逆变换的复杂度都可以接受.

我们设,我们想要做的就是构造一组满足上述条件的.

考虑:

再考虑:

比较两边系数,有.只要满足这个条件,我们就能构造出一组满足条件(1)的线性变换.如果这个线性变换对应的矩阵可逆,那么就满足了条件(2).

而由于是集合的运算,我们可以对二进制分开考虑.换句话说,如果,那么一定是满足条件的.

这样我们就可以只求一个的线性变换矩阵就好.我们接下来将对三种常见的基础位运算()分别讨论这个矩阵.我们先来解决第三个问题:如何快速求出呢?

考虑和FFT一样折半,令的最高位是否是,去掉最高位后的二进制数字,令我们有:

这样就实现了规模减半,复杂度.

下面我们设FWT的变换矩阵为.顺便一提,不难发现,最后对整体做的矩阵是这个矩阵的克罗内多积.

或卷积

取矩阵.其逆矩阵为.

与卷积

取矩阵.其逆矩阵为.

异或卷积

取矩阵.其逆矩阵为.

生成函数角度

我们再从生成函数角度理解一下FWT.

我们重新定义幂乘法:,显然幂乘法该满足的性质它都满足.

观察FWT的式子:

这等价于:

子集卷积

子集卷积:.

意识到该卷积与或卷积的差别在于:或卷积会多累加一些的答案,而.

因而可以将原集合按照元素个数分组做FMT,然后再次结合,并做IFMT,最终将结果累计,复杂度.

集合占位幂级数

其实就是设,然后做卷积(类似子集卷积).

Example

Example1([AGC034F] RNG and XOR)

表示操作次后第一次变成的概率,表示操作次后变成的概率.分别是其生成函数.

注意到,而就是期望.接下来的问题在于如何求.

接下来涉及到的东西就很本质了,我们一开始先把,然后做,注意这里,FWT自身有很好的性质:.我们做次操作后得到的概率数组也就是.展开FWT的式子,自然有:

(我草这个式子太顶级了)

但是我们冷静一下,这个题与普通生成函数不同的地方在于,我们要求,因此我们直接把带入算一算就好.不过由于,我们必须要解决分母为的情况,解决的方法是分母乘上,这样就消掉了的项,同时分子由于是减法可以抵消一下.

然后大概做做吧,感觉太顶级了.

Example2([QOJ5089]环覆盖)

合法显然当且仅当每个点度数为偶数,考虑直接拿一个二进制数将每个点度数奇偶性压起来,如果选中一条边就异或上.最后要求这个二进制数是.我们用一个二元组表示在集合幂级数上异或上,在多项式上乘上.显然一条边是.注意到这是可以定义乘法运算和标量乘法运算的,也就能做FWT,而且在做FWT的时候要么乘上要么乘上,做完FWT得到的每一个一定形如,做IFWT的时候直接求即可.

仔细想想这个过程:有一句名言是只要看到生成函数就一定存在分配律,这里也是一样的,由于存在一种选择:选不选这条边,因此这里也就有了两种情况:,分开两种情况就实现了FWT.

问题在于对于每个,也就是对于每个求有多少条边满足,也就是求有多少条边一段链接在了的内部,另一端连接在了外部,这个补集转化一下,做高维前缀和.

Example3(CF1034E Little C Loves 3 III)

仍然是子集卷积,转化为.然后我们将乘上,将乘上,最后把除去取膜就行.

还有个用到FWT的本质的矩阵做法,大概是手推矩阵然后再手推求逆.

Example4(CF1336E2 Chiori and Doll Picking)

先考虑easy version.首先求出线性基,如果线性基的大小比较小,我们可以直接枚举一下.而如果线性基较大,我们先消成最简线性基,然后主元位置有多少个取决于选了多少个元素,其他位置共有个,可以直接状压进状态.这样复杂度就是.

那么我们怎么优化呢?首先较大的时候有点难做,我们看看能不能优化到.

考虑设,将线性基能做出的线性空间设为,当且仅当线性基能异或出(最后再把那些废元素贡献到答案里).那么的答案就是.考虑,问题在于这个东西好像也不好做.

然后接下来开始一波顶级操作(下面的操作全部基于行向量+行操作):

引理1:要么是,要么是.

考虑:

这句是为啥呢?因为对于右边的每一个数字和左边的一个数字,如果它们都在线性基中,一定存在一个数字满足,不然就是.

于是我们有:

引理2:.

直接展开上面的式子,用.

引理3:中值为的位置构成一个线性基.

只需要证明封闭性就好,注意到如果满足条件,满足条件,一开始做FWT时我们已经注意到:.于是这个引理也显然成立.

引理4:中值为的位置构成的线性基的大小是.

设这些位置构成的空间是,当且仅当在这个空间中.我们有:

注意到,也就是,这就证明了引理.

引理5:将的线性基对应的矩阵从前往后消成最简,的线性基对应的矩阵从后往前消成最简,上拼成一个的矩阵,那么这个矩阵关于主对角线对称.

首先根据可以知道主对角线一定全是,然后我们任取中的一个基中的一个基,应该有.不难发现此时必定对称(画个图,不对称的话考虑主元对他俩的贡献就不是偶数了).

通过这个引理可以由得知长什么样.

引理6:只和有关.

因为只和有关,这里考虑一下对称性就可以.因此设.

然后注意到.组合意义展开一下:

接下来怎么做呢?令,这里可以,然后乘起来就行了.

太顶级了吧.

Example5(CF 1326F2)

首先发现"如果没有边那么是"这个限制太强了,如果我们能改为"如果是,那么可有边可无边"的话,整个序列就会被的段分成若干两两无关的链.显然这是一步或卷积,这样我们就只需要求后者.如果设表示长度为,一段长度为的连续的对应的集合是的方案数,不难发现我们最后只需要做一个类似子集卷积的东西就行(前面的每个段会自动在后面放个).

但是还没完,题目让我们求每一个,我们不难发现我们这样划分之后答案只取决于链的长度的可重集合,而本质不同的集合的数量很少,直接枚举就行.

Example6(qoj5019)

首先可以类似数位dp设计一个表示目前dp到了第位,然后前面的.接下来分类讨论当前的最大值限制是还是.

这个题知道题解其实没什么难的,但是这个题告诉了我们:FWT作为一种线性变换,它是可以和其它线性变换一起做的,也就是说你是可以将其中的若干位做FWT,剩下若干位做其它的东西的.

生成函数

普通生成函数(OGF)

概念

我们定义一个幂级数形如,并使.则称的生成函数.

运算

  1. .

  2. .

  3. .

  4. .

  5. .

  6. .

  7. .

常见序列生成函数

  1. ,.

证明显然.

  1. ,.

证明根据二项式定理.

  1. .

证明显然.

  1. ,

直接使用二项式定理展开,可以得到:

反转上指标并使用对称恒等式得到上式.此外上式还有两个特殊形式:

根据求导即可得到此式.

  1. .

  2. .

  3. .

可以使用积分或泰勒展开证明.

  1. .

也即卡特兰数的生成函数,证明考虑:

然后得到两个根,带入舍掉一个.

指数生成函数(EGF)

https://zhuanlan.zhihu.com/p/53079223

序列的指数生成函数定义为形式幂级数.注意.

基本运算

我们有:

的EFG.

注意到有一个特例是就是的EGF.

封闭式

直接泰勒展开就可以得到

换元后可以得到.一个经典特例是.

  1. .

显然.

  1. .

做二项式定理就显然了.

  1. .

  2. .

都可以通过泰勒展开证明.

EXP的组合意义

我们设个有标号元素划分成个非空无序集合的情况,个元素组成一个集合的时候,其上特定组合结构的数量(就是一个一个只和有关的定义在集合上的函数),有:

,再设:

或者直接递推:

简而言之,是将个有标号的元素放到同一个无序集合的方案数,而是将个有标号的元素分成若干个无编号的非空无序集合的方案数.

Example

Example1(POJ3734)

对于红黄色砖块,其选取方案为,对应的EGF是.

对于蓝绿色砖块,选取方案是.

乘起来有:

于是有.

Example2(圆排列)

长度为的排列数的指数生成函数是.

长度为的圆排列的指数生成函数是.

于是有.

这个怎么理解呢?考虑一个排列可以分成若干个置换环,而一个集合能形成的置换环数量显然就是圆排列.

Example3(错排数)

从置换环的角度考虑,错排是指置换环中不存在自环的排列,也就是说不存在长度为的置换环,其EGF显然是,错排数的EGF对其取即可.

Example4(点带编号无向连通图计数)

考虑如果个点带编号的无向连通图的EGF是,那么个点带标号无向图的EGF就是,后者直接计数,前者对后者做一次就好.

Example5(不动点计数)

求有多少个映射满足(共)(共).

考虑将,这等价于对深度不超过的基环树(环的长度为)计数,等价于对深度不超过的有根树计数.注意到删去根节点后等价于对深度不超过的有根树计数,因此.

Example6([CF891E]Lust)

假设次操作后减少了,实际上要求的就是.

考虑对所有情况下的求和,注意到次操作,使得出现次的方案数是.直接设的EGF是

答案就是.

Example7

狄利克雷生成函数(DGF)

对于序列,定义其DGF为.注意到若是积性函数,那么.

基本运算

对于两个序列,其DGF之积对应的是两者的狄利克雷卷积序列的DGF:

封闭式

  1. .

显然为.

  1. .

其封闭式是黎曼函数,事实上,我们有:

  1. .

其DGF为.注意到.

  1. .

.

  1. .

  1. .

注意到:

也注意到.

  1. .

注意到,也就是说.

  1. .

.

Example

Example1(luoguP3768)

考虑对于构造积性函数使得.

注意到:

也就是.

阶乘的扩展定义

对于复数的阶乘,我们通常定义:

同时我们定义,有:.

这样我们还可以定义广义阶乘幂:

通过以上我们还可以有二项式系数的定义:

超几何级数

超几何函数

我们定义超几何函数.

许多生成函数都可以写成超几何函数的形式.

值得一提的是,如果我们直接定义类似之类的式子,可以发现当时任意超几何函数总是.

值得一提的是,我们通常直接忽略超几何函数中的任何特殊情况,例如分母为或者哪里出现了正无穷.如同生成函数中我们不关心式子带入一个数后是否收敛而只关心是否两边存在一种对应的转化.如果你想去探究超几何函数中的各种情况,那请去翻那本巨大的、黑糊糊的、作者默认你精通高等数学的、你在第五章看到的恒等式作者在第七章才给出证明的《具体数学》,而不是看我这个弱智写的笔记.笔者看这一段的时候已经被作者的"严谨"态度整疯了.该证明的一拖再拖不该证明的可以一句话带过的逼逼逼逼.仿佛这本书就是写给那些已经会了所有东西只是来使自己已经学会的东西更加体系化的以及拿来查阅各种恒等式的工具书.

特殊的超几何函数

合流超几何函数

我们通常把形如的函数称为合流超几何函数.

不难发现我们有:

也即常见生成函数中的.

高斯超几何函数

我们把形如的函数称为高斯超几何函数.

下面是几种常见的高斯超几何函数形式:

  1. .

即常见生成函数.

  1. .

即常见生成函数.

  1. .

即常见生成函数.

  1. .

即常见生成函数.

超几何级数的应用

我们先考虑改写超几何级数的形式:

.

不难发现,而:

换句话说,是关于的一个有理函数.而根据代数基本定理,任意的有理函数在内都可以分解为以上的形式(如果缺少项则需要上下同时乘以以补上).

换句话说,对于一个无穷级数,我们先将表示回超几何函数,设为.

那么有:.

好,接下来请把脑子扔了,不要纠结某一个公式按理来说只能作用于正整数而在这里直接将它套在了复数域上.你只需要知道数学家非常厉害通过扩展一些东西的定义(大部分是阶乘和函数的定义)来使这些公式全部成立.But who cares,只要我们能用就行了.我又不是数竞人,这些麻烦的要死我直观上根本无法接受的定义交给数竞老哥证明得了.反正信竞更多依赖离散数学,大不了我们直接默认所有的数都是自然数.

Example

求证:.

首先考虑:

有了这个无穷级数,我们可以直接将二项式系数用阶乘形式展开,于是得到:

两边同时除以得到上式.

二项式系数与超几何函数

通过范德蒙德卷积,不难验证:

这个公式的一个特例是:

这个公式几乎囊括了所有的基本二项式求和公式:上指标求和,平行求和,范德蒙德卷积......几乎只要是我们推出的由两个及以下项的乘积求和的二项式系数相关的式子都可以使用这个式子.

那么如果我们要三项相乘或更多,我们有Saalschütz恒等式:

事实上,《具体数学》上给出了大量的超几何级数的应用以及各种技巧,但是笔者的智商已经理解不了接下来的内容了.考虑到大部分时候上述两个超几何级数恒等式已经足够解决绝大部分问题,如果考到更加困难的求和技巧,笔者相信别人也不会做,于是笔者决定摆烂.

求微分方程

Example1(luogu4931)

二项式反演:

注意到后者只与有关,不妨设其为,预处理一下就可以做到.

加强版咋做?我们继续看看式子:

注意到是一个卷积的形式,设其生成函数为,,我们自然有.

考虑的生成函数形式,先看,显然用泰勒展开:

再看,是卡特兰数的生成函数,有:

这下简单了,答案是:

现在看,平方一下有:

两边求导:

得到了一个线性递推形式,更进一步地:

技术总结一下:其实就是你想要得到一个递推式,然后注意到这玩意要写成微分方程的形式,所以开始往那边凑.

生成函数的应用

求解递归关系

我们假设已经有了,并且,其中都是多项式,我们想要找到一种方式求解.

考虑有理函数,不难发现.

那么可以证明,只要无重根并且无零根,那么就存在一组系数满足.

我们这么定义"反射"运算,若,则其反射多项式为.

,则显然有.

那么显然这里求出来的这组数就是中的那组.

而我们有.

Example1

已知,求.

首先两边同时除以并将组合数用阶乘形式展开,我们有:

如果我们令,则有:

于是.

Example2([QOJ5169] 夹娃娃)

首先设为第家的生成函数,这个是显然可以快速预处理出来的.令.

问题在于每次询问的时候求出答案呢?

这里有一个套路:我们在一开始就暴力做点值,最后拿拉格朗日插值求答案.中间大概把能预处理的都预处理一下.最后的问题在于:

第一,预处理点值的时候,一共有个多项式,最高次数是,因此一共要插入个值,又要处理每个后缀,复杂度来到.这个问题是好解决的.我们只需要在带入点值的时候做一个后缀继承一类的东西,复杂度就可以来到.

第二,询问的时候需要找到所有对应的点值并暴力乘起来,复杂度来到.但如此小,我们可以用指数级别的复杂度来优化,我们考虑预处理一下的答案,复杂度来到.但是这个更不太行.那怎么办呢?我们把这个指数级别的东西分块一下.预处理复杂度来到,单次询问复杂度来到.但这个预处理复杂度好像还是有点艰难.不过注意到如果做一个剪枝优化:如果总共的喜欢的店的个数乘以要大于,就直接输出.预处理的时候块内部也做一个剪枝,然后发现就能过了(牛逼).

第三,拉格朗日插值的时候需要的复杂度,不过由于点值可以自己控制,这个复杂度可以轻松降到.

Example3([十二省联考 2019] 皮配)

首先注意到题目等价于规定一个阵营和一个排序的人数上下界.

我们可以将这四位导师分别记为,这样最后判断幂在一个区间内的前面的系数就行.

注意到如果没有学校有偏好,将生成函数卷起来后得到的答案就是.也就是是互相独立的,我们可以分开算.

对于那些有偏好的学校,我们暴力算就行.复杂度不会高于.最后两部分合并一下.