@总结 - [email protected] 拉格朗日反演与复合逆

目录

  • @0 - 参考资料@
  • @1 - 问题描述@
  • @2 - 拉格朗日反演@
    • @2.1 - 解法及证明@
    • @2.2 - 典例@
  • @3 - 扩展拉格朗日反演@
    • @3.1 - 解法及证明@
    • @3.2 - 典例@

@0 - 参考资料@

参考博客-1
zjt 的博客地址挂掉了,所以这里暂时没有。

@1 - 问题描述@

我们知道,有时候可以使用牛顿迭代法求 G(F(x)) = 0 的某个根 F0(x)。

但是有时候函数 G 是一个非常复杂的多项式。
这个时候使用牛顿迭代法,每一层的复杂度可能要做 n 次乘法与加法(n 为当前多项式长度)。
于是时间复杂度将会急剧退化,甚至不如暴力。

但假如 G(F(x)) = x,其中 G, F 都为多项式函数,在已知 F 的情况下,我们可以使用拉格朗日反演求出 G 的某一项。

同时,假如 G(F(x)) = H(x),我们也可以使用扩展拉格朗日反演求出在已知 F 的情况下求 G 的某一项。

@2 - 拉格朗日反演@

@2.1 - 解法及证明@

形式化地给出问题:

若已知多项式函数 F(x),且复合函数 G(F(x)) 满足 G(F(x)) = x,求 G(x) 的第 n 项。

此时可以看作是 x 在复合函数 G·F 的作用下保持不变,所以我们称 G 为 F 的复合逆(复合运算下的逆元)。
不过对于 F,G 是有要求的:要求两个多项式常数项为 0 且一次项系数不为 0。不然复合逆是不存在的。

有一个结论:若 F 为 G 的复合逆,则 G 为 F 的复合逆。即 F(G(x)) = x 意味着 G(F(x)) = x。
所以:我们称 F 与 G 互为复合逆。
这个技巧可以在已知 F 的情况下,将 F(G(x)) = x 转为 G(F(x)) = x,有助于我们解决一些题目。

开始描述解法:
令 \(G(x) = \sum_{i=1}a_i*x^i\),那么就有:
\[\sum_{i=1}a_i*F^i(x) = x\]

两边求导,得到:
\[\sum_{i=1}i*a_i*F^{i-1}(x)*F'(x) = 1\]

为了得到 G 的第 n 项 \(a_n\),我们将两边除以 \(F^n(x)\),得到:
\[\sum_{i=1}i*a_i*F^{i-n-1}(x)*F'(x) = \frac{1}{F^n(x)}\]

(这个地方注意一点:我们此时函数并不一定为多项式——我们将函数推广到了负整数幂)
对于幂函数 \(F^a(x)\),当 a ≠ 0 时,它的导数为 \(a*F^{a-1}(x)*F'(x)\)。
反过来,当 b ≠ -1 时,\(F^{b}(x)*F'(x)\) 的积分为 \(\frac{F^{b+1}(x)}{b+1}\)。

于是对上面的式子进行进一步转换:
\[n*a_n*\frac{F'(x)}{F(x)}+\sum_{i=1}^{i\not = n}\frac{i}{i-n}*a_i*(F^{i-n}(x))' = \frac{1}{F^n(x)}\]

等式左右对应项系数相等。假如我们取 \(x^{-1}\) 的系数,则等式左边只留下 \(n*a_n*\frac{F'(x)}{F(x)}\) 这一部分(另一部分因为幂函数求导不会出现 \(x^{x-1}\) 这一项)。

于是就有:
\[[x^{-1}](n*a_n*\frac{F'(x)}{F(x)}) = [x^{-1}](\frac{1}{F^n(x)})\]

其中\([x^{-1}]\) 表示取 \(x^{-1}\) 的系数。

然后我们考虑一下 \(\frac{F'(x)}{F(x)}\) 中 \(x^{-1}\) 的系数:
\[
\begin{align}\frac{F'(x)}{F(x)}&=\frac{a_1+2a_2x+3a_3x^2+\cdots}{a_1x+a_2x^2+\cdots}\&=\frac{a_1+2a_2x+3a_3x^2+\cdots}{a_1x}\cdot \frac{1}{1+\left(\frac{a_2}{a_1}x+\frac{a_3}{a_1}x^2+\cdots\right)}\&=\left(x^{-1}+\frac{2a_2}{a_1}+\cdots\right)\left(1-x\left(\frac{a_2}{a_1}+\frac{a_3}{a_1}x+\cdots\right)\right)
\end{align}
\]

总之经过一系列代数变形得到 \([x^{-1}]\frac{F'(x)}{F(x)} = 1\)。
于是就有:
\[n*a_n =[x^{-1}](\frac{1}{F^n(x)})\]
\[a_n = \frac{1}{n}*[x^{-1}](\frac{1}{F^n(x)}) = \frac{1}{n}*[x^{n-1}](\frac{1}{(\frac{F(x)}{x})^n})\]

跑一个多项式逆元与多项式 exp + ln 求 k 次幂即可。

@2.2 - 典例@

bzoj3684:大朋友和多叉树
题目点这里查看

根据题目给出的定义,设权值为 i 的结点为根的树方案数为 {ai},设 f(x) 为 {ai} 的生成函数。
则有:
\[f(x) = \sum_{k\in D}f^k(x) + x\]

其中最后一个 x 表示叶子结点的方案数。
稍微变形就可以得到复合逆的基本形式 \(g(f(x)) = x\),直接复合逆即可。
代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int MOD = 950009857;
const int MAXN = 400000;
const int G = 5;
int pow_mod(int b, int p) {
    int ret = 1;
    while( p ) {
        if( p & 1 ) ret = 1LL*ret*b%MOD;
        b = 1LL*b*b%MOD;
        p >>= 1;
    }
    return ret;
}
struct poly{
    int pw[20 + 5], ipw[20 + 5];
    poly() {
        for(int i=0;i<=21;i++)
            pw[i] = pow_mod(G, (MOD-1)/(1<<i)), ipw[i] = pow_mod(pw[i], MOD-2);
    }
    void debug(int *A, int n) {
        for(int i=0;i<n;i++)
            printf("%d ", A[i]);
        puts(""), puts("");
    }
    void clear(int *A, int l, int r) {
        for(int i=l;i<r;i++)
            A[i] = 0;
    }
    void copy(int *A, int *B, int n) {
        for(int i=0;i<n;i++)
            A[i] = B[i];
    }
    int length(int n) {
        int len; for(len = 1; len < n; len <<= 1);
        return len;
    }
    void ntt(int *A, int n, int type) {
        for(int i=0,j=0;i<n;i++) {
            if( i < j ) swap(A[i], A[j]);
            for(int k=(n>>1);(j^=k)<k;k>>=1);
        }
        for(int i=1;(1<<i)<=n;i++) {
            int s = (1<<i), t = (s>>1);
            int u = (type == 1) ? pw[i] : ipw[i] ;
            for(int j=0;j<n;j+=s) {
                for(int k=0,p=1;k<t;k++,p=1LL*p*u%MOD) {
                    int x = A[j+k], y = 1LL*p*A[j+k+t]%MOD;
                    A[j+k] = (x + y)%MOD, A[j+k+t] = (x + MOD - y)%MOD;
                }
            }
        }
        if( type == -1 ) {
            int iv = pow_mod(n, MOD-2);
            for(int i=0;i<n;i++)
                A[i] = 1LL*A[i]*iv%MOD;
        }
    }
    int tmp1[MAXN + 5], tmp2[MAXN + 5];
    void poly_mul(int *A, int *B, int *C, int n, int m, int k) {
        int len = length(n + m - 1);
        clear(tmp1, 0, len), clear(tmp2, 0, len);
        for(int i=0;i<n;i++) tmp1[i] = A[i];
        for(int i=0;i<m;i++) tmp2[i] = B[i];
        ntt(tmp1, len, 1), ntt(tmp2, len, 1);
        for(int i=0;i<len;i++)
            C[i] = 1LL*tmp1[i]*tmp2[i]%MOD;
        ntt(C, len, -1);
    }
    int tmp3[MAXN + 5];
    void poly_inv(int *A, int *B, int n) {
        if( n == 1 ) {
            B[0] = pow_mod(A[0], MOD-2);
            return ;
        }
        poly_inv(A, B, (n+1)>>1);
        poly_mul(A, B, tmp3, n, (n+1)>>1, n);
        for(int i=0;i<n;i++)
            tmp3[i] = (MOD - tmp3[i])%MOD;
        tmp3[0] = (tmp3[0] + 2)%MOD;
        poly_mul(tmp3, B, B, n, (n+1)>>1, n);
    }
    void poly_int(int *A, int *B, int n) {
        for(int i=n-1;i>0;i--)
            B[i] = 1LL*pow_mod(i, MOD-2)*A[i-1]%MOD;
        B[0] = 0;
    }
    void poly_der(int *A, int *B, int n) {
        for(int i=0;i<n-1;i++)
            B[i] = 1LL*(i+1)*A[i+1]%MOD;
        B[n-1] = 0;
    }
    int tmp4[MAXN + 5];
    void poly_ln(int *A, int *B, int n) {
        poly_inv(A, tmp4, n);
        poly_der(A, B, n);
        poly_mul(B, tmp4, B, n, n, n);
        poly_int(B, B, n);
    }
    int tmp5[MAXN + 5];
    void poly_exp(int *A, int *B, int n) {
        if( n == 1 ) {
            B[0] = 1;
            return ;
        }
        poly_exp(A, B, (n+1)>>1), clear(B, (n+1)>>1, n);
        poly_ln(B, tmp5, n);
        for(int i=0;i<n;i++)
            tmp5[i] = (A[i] + MOD - tmp5[i])%MOD;
        tmp5[0] = (tmp5[0] + 1)%MOD;
        poly_mul(tmp5, B, B, n, (n+1)>>1, n);
    }
    int tmp6[MAXN + 5];
    void poly_pow(int *A, int *B, int n, int k) {
        poly_ln(A, tmp6, n);
        for(int i=0;i<n;i++)
            tmp6[i] = 1LL*k*tmp6[i]%MOD;
        poly_exp(tmp6, B, n);
    }
}op;
int s, m, f[MAXN + 5], g[MAXN + 5];
int main() {
    scanf("%d%d", &s, &m);
    for(int i=1;i<=m;i++) {
        int x; scanf("%d", &x);
        f[x] = (MOD - 1);
    }
    f[1] = 1;
    for(int i=0;i<s;i++)
        f[i] = f[i+1];
    op.poly_pow(f, f, s, s);
    op.poly_inv(f, g, s);
    printf("%lld\n", 1LL*g[s-1]*pow_mod(s, MOD-2)%MOD);
}

@3 - 扩展拉格朗日反演@

@3.1 - 解法及证明@

形式化地给出问题:

若已知多项式函数 F(x),H(x),且复合函数 G(F(x)) 满足 G(F(x)) = H(x),求 G(x) 的第 n 项。

基本上和拉格朗日反演的推导过程是一样的,最后得到:
\[n*a_n =[x^{-1}](\frac{H'(x)}{F^n(x)})\]
\[a_n = \frac{1}{n}*[x^{n-1}](\frac{H'(x)}{(\frac{F(x)}{x})^n})\]

不过这里有一个常用的变换形式:
令 \(x' = F(x)\),则 \(x = F^{-1}(x')\)。这意味着我们可以将复合函数内部的某个函数“置换”出来。

@3.2 - 典例@

咕。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11233318.html

时间: 2024-07-29 21:47:12

@总结 - [email protected] 拉格朗日反演与复合逆的相关文章

BZOJ 3684 大朋友和多叉树 FFT+拉格朗日反演

题目大意:给定n和集合S,求满足下列要求的多叉树的个数: 1.每个非叶节点的子节点数量在集合S中 2.每个叶节点的权值为1,每个非叶节点的权值为子节点权值之和 3.根节点的权值为n 注意每个节点的子节点有顺序 令fi表示根节点权值为i的神犇二叉树个数,F(x)为fi的生成函数,C(x)为S的生成函数,那么有: F(x)=∑i∈SFi(x)+x F(x)=C(F(x))+x F(x)?C(F(x))=x 不妨令G(x)=1?C(x) 那么有: G(F(x))=x 因此F(x)是G(x)的复合逆 拉

[拉格朗日反演][FFT][NTT][多项式大全]详解

1.多项式的两种表示法 1.系数表示法 我们最常用的多项式表示法就是系数表示法,一个次数界为\(n\)的多项式\(S(x)\)可以用一个向量\(s=(s_0,s_1,s_2,\cdots,s_n-1)\)系数表示如下:\[S(x)=\sum_{k=0}^{n-1}s_kx^k\] 系数表示法很适合做加法,可以在\(O(n)\)的时间复杂度内完成,表达式为:\[S(x)=A(x)+B(x)=\sum_{k=0}^{n-1}(a_k+b_k)x^k\] 当中\[s_k=a_k+b_k\] 但是,系数

【CTF】Reverse [email&#160;protected]

来源: 360 CTF 2014 Reverse20 说明:已在压缩包中给定了一个用ReverseMe.exe加密过后的文件:密文.db请分析ReverseMe.exe的算法,写出解密算法,解密该文件得到Key.该Exe里有一个bug,导致exe无法运行:提示:你有两种方法得到该Key:1.找到bug,patch掉之后,运行两次该程序即可解密文件得到key.2.老老实实的逆这个简单的算法,写出一个解密程序,解密. 答案: [CTF]Reverse [email protected]

@codechef - [email&#160;protected] Random Number Generator

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 给定递推关系式:\[A_i=C_1A_{i-1} + C_2A_{i-2}+\dots+C_kA_{i-k}\] 并给定 \(A_

@算法 - [email&#160;protected] matrix - tree 定理(矩阵树定理)

目录 @0 - 参考资料@ @0.5 - 你所需要了解的线性代数知识@ @1 - 定理主体@ @证明 part - [email protected] @证明 part - [email protected] @证明 part - [email protected] @证明 part - 4@ @2 - 一些简单的推广@ @3 - 例题与应用@ @0 - 参考资料@ MoebiusMeow 的讲解(超喜欢这个博主的!) 网上找的另外一篇讲解 @0.5 - 你所需要了解的线性代数知识@ 什么是矩阵

拉格朗日反演证明

感谢 BZT 大仙的细心指导: →_→ 求函数 G 满足: \[G(F(x))=x\] 其中 G 和 F 都要满足常数项为 0 且 1 次项不为 0 设 \(G(x)=\sum_{i>=1} a_i x^i\) 那么原式就是: \[\sum_{i=1}^\infty a_i F^i(x)=x\] 然后我们两边取导: \[\sum_{i=1}^\infty i·a_i F^{i-1}(x) F'(x)= 1 \] 然后左右除去 \(F^n(x)\) : \[\sum_{i=0}^{\infty}i

@hdu - [email&#160;protected] Function

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定 n,求: \[\sum_{i=1}^{n}gcd(\lfloor^3\sqrt{i}\rfloor, i)\mod 998244353\] Input 第一行包含一个整数 T(1≤T≤11) 描述数据组数. 接下来 T 行,每行一个整数 n (1≤n≤10^21) 描述询问. O

@codeforces - [email&#160;protected] Vladislav and a Great Legend

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树 T.对于每一个非空点集 X,定义 f(X) 为包含 X 内所有点的最小连通块的边数. 另给定一正整数 k,求: \[\sum\limits_{X \subseteq \{1, 2,\: \dots \:, n\},\, X \neq \varnothing} (

@uoj - [email&#160;protected] 【UNR #2】黎明前的巧克力

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] Evan 和 Lyra 都是聪明可爱的孩子,两年前,Evan 开始为一个被称为UOJ的神秘的OI组织工作,在 Evan 与其他小伙伴的努力下,UOJ不仅成了OI界原创比赛的典范,更是因UR这一反人类难度的存在而举世闻名.然而今年,随着 Evan 前往世界彼岸,UOJ一天天减少着他的活力