多项式复合和拉格朗日反演 学习笔记
20220520
一些小东西
卡特兰数和去掉根的二叉树对应。考虑卡特兰数的递推公式 \(f(n) = \sum_{i = 0}^{n - 1} f(i) f(n - 1 - i)\),并有 \(f(0) = f(1) = 1\)。
多项式复合
复合运算
幂级数的复合运算。
设 \(A(w)=\sum_{i\ge 0} a_i w^i\),\(B(x)=\sum_{i \ge 1}b_i x ^ i\)。则 \(B(x)\) 与 \(A(w)\) 的复合就是
可以将其整理为 \(c_i = \sum_{i \ge 0} c_i x ^ i\) 的形式。
由于假定 \(b_0 = 0\),于是即使 \(A\) 有无限多个非 \(0\) 项, \(B(x)\) 和 \(A(w)\) 的复合可以定义。
多项式复合函数
给定 \(f(w), g(x)\),如果直接用定义计算 \(f(g(x))\) 的前 \(n\) 项系数,时间复杂度为一个常数大的 \(O(n ^ 2\log n)\) 。
下面是一个复杂度为 \(O(N ^ 2)\) 的算法,运用了大步小步的算法思想。
令 \(d = \lceil \sqrt n\rceil\),先求出来 \(g(x) ^ 2, g(x) ^ 3, \ldots , g(x) ^ d\),复杂度是 \(O(d n\log n)\),同时计算 \(g(x) ^ {2d},g(x)^{3d},\ldots ,g(x) ^ {d(d - 1)}\),复杂度为 \(O(d n\log n)\)。
接下来,根据
然后发现,如果我暴力计算 \(\sum_{0\le j <d} f_{id + j} g(x)^j\) 然后再暴力把前面的式子加起来,一共的多项式加法次数不会超过 \(O(N)\),于是这部分复杂度为 \(O(N ^ 2)\)。
如是说,我们在 \(O(N ^ 2 + n \sqrt n \log n)\) 的情况下计算了多项式复合逆。
题目:P5373 【模板】多项式复合函数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
mt19937 eng;
void init(int Seed) {return eng.seed(Seed);}
int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++);
template<typename T>
inline void read(T &x) {
x = 0;
bool f = 0;
char ch = gc();
while (!isdigit(ch)) f = ch == '-', ch = gc();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
if (f) x = -x;
}
inline void reads(char *s) {
char ch = gc();
while (isspace(ch)) ch = gc();
while (!isspace(ch)) *(s++) = ch;
return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
read(x);
read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 262144, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) return x - MOD;
else if (x < 0) return x + MOD;
else return x;
};
template<const int N_num, const int M_num>
struct Graph {
int H[N_num];
struct Edge {int to, lac;} e[M_num];
inline void add_edge(int x, int y) {e[*H] = {y, H[x]};H[x] = (*H)++;}
inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
int fac[MAXN], ifac[MAXN];
namespace POLY {
int SZ, R[MAXN], W[MAXN], INV[MAXN + 1];
void POLYINIT() {
INV[1] = 1;
for (int i = 2; i <= MAXN; ++i) INV[i] = (long long) (MOD - MOD / i) * INV[MOD % i] % MOD;
}
void INIT(int len) {
if (SZ == len) return;
SZ = len;
for (int i = 1; i < len; ++i) R[i] = (R[i >> 1] >> 1) | (i & 1 ? (len >> 1) : 0);
int wn = Ksm(3, (MOD - 1) / len);
W[len >> 1] = 1;
for (int i = (len >> 1) + 1; i < len; ++i) W[i] = (long long) W[i - 1] * wn % MOD;
for (int i = (len >> 1) - 1; i > 0; --i) W[i] = W[i << 1];
}
unsigned long long c[MAXN];
void Ntt(vector<int>& F, int limit, int type) {
copy(F.begin(), F.begin() + limit, c);
for (int i = 1; i < limit; ++i) if (i < R[i]) swap(c[i], c[R[i]]);
for (int o = 2, j = 1; o <= limit; o <<= 1, j <<= 1) {
for (int i = 0; i < limit; i += o) {
for (int k = 0; k < j; ++k) {
unsigned long long OI = c[i + j + k] * W[k + j] % MOD;
c[i + j + k] = c[i + k] + MOD - OI;
c[i + k] += OI;
}
}
}
if (type == -1) {
reverse(c + 1, c + limit);
int inv = INV[limit];
for (int i = 0; i < limit; ++i) c[i] = c[i] % MOD * inv % MOD;
}
for (int i = 0; i < limit; ++i) F[i] = c[i] % MOD;
}
struct Poly {
vector<int> v;
int& operator [] (const int &pos) { return v[pos]; }
int len() { return v.size(); }
void set(int l) { return v.resize(l); }
void adjust() { while (v.size() > 1 && !v.back()) v.pop_back(); }
void rev() { reverse(v.begin(), v.end()); }
void Ntt(int L, int type) {
int limit = 1 << L;
INIT(limit);
set(limit);
POLY::Ntt(v, limit, type);
}
void Squ() {
int L = ceil(log2(len())) + 1, limit = 1 << L;
Ntt(L, 1);
for (int i = 0; i < limit; ++i) v[i] = (long long) v[i] * v[i] % MOD;
Ntt(L, -1);
adjust();
}
void operator += (Poly &x) {
if (len() < x.len()) set(x.len());
for (int i = 0; i < x.len(); ++i) v[i] = Mod(v[i] + x[i]);
adjust();
}
void operator -= (Poly &x) {
if (len() < x.len()) set(x.len());
for (int i = 0; i < x.len(); ++i) v[i] = Mod(v[i] - x[i]);
adjust();
}
Poly operator * (Poly &x) {
Poly ret, tmp0 = *this, tmp1 = x;
int L = ceil(log2(tmp0.len() + tmp1.len() - 1)), n = 1 << L;
tmp0.Ntt(L, 1);
tmp1.Ntt(L, 1);
ret.set(n);
for (int i = 0; i < n; ++i) ret[i] = (long long) tmp0[i] * tmp1[i] % MOD;
ret.Ntt(L, -1);
ret.adjust();
return ret;
}
Poly operator - (Poly &x) {
Poly ret;
ret.set(max(len(), x.len()));
for (int i = 0; i < len(); ++i) ret[i] = v[i];
for (int i = 0; i < x.len(); ++i) ret[i] = Mod(ret[i] - x[i]);
return ret;
}
Poly operator * (int x) {
Poly ret = *this;
for (auto &i: ret.v) i = (ll) i * x % MOD;
return ret;
}
Poly operator + (Poly &x) {
Poly ret;
ret.set(max(len(), x.len()));
for (int i = 0; i < len(); ++i) ret[i] = v[i];
for (int i = 0; i < x.len(); ++i) ret[i] = Mod(ret[i] + x[i]);
return ret;
}
void operator *= (Poly &x) {
Poly tmp = x;
int L = ceil(log2(len() + x.len() - 1)), n = 1 << L;
Ntt(L, 1);
tmp.Ntt(L, 1);
for (int i = 0; i < n; ++i) v[i] = (long long) v[i] * tmp[i] % MOD;
Ntt(L, -1);
adjust();
}
};
}
using namespace POLY;
int N, M;
Poly f, g, b1[150], b2[150];
int main() {
// std::ios::sync_with_stdio(0);
// cout << std::fixed << std::setprecision(8);
// cin.tie(0);
// cout.tie(0);
freopen("1.in", "r", stdin);
// freopen("1.out", "w", stdout);
read(N);
read(M);
++N;
++M;
qwq::init(20050112);
int lim = ceil(sqrt(N));
POLYINIT();
f.set(N);
g.set(M);
for (int i = 0; i < N; ++i) read(f[i]);
for (int j = 0; j < M; ++j) read(g[j]);
b1[0].set(N);
b1[0][0] = 1;
for (int i = 1; i < lim; ++i) b1[i] = b1[i - 1] * g, b1[i].set(N);
Poly tmp = g * b1[lim - 1];
tmp.set(N);
b2[0].set(N);
b2[0][0] = 1;
for (int i = 1; i < lim; ++i) b2[i] = b2[i - 1] * tmp, b2[i].set(N);
Poly ans;
ans.set(N);
for (int i = 0; i < lim; ++i) {
Poly ret;
ret.set(N);
for (int j = 0; j < lim; ++j) {
if (i * lim + j >= N) break;
Poly tmp = b1[j] * f[i * lim + j];
ret += tmp;
// ret.set(N);
}
ret *= b2[i];
ret.set(N);
ans += ret;
}
ans.set(N);
for (int i = 0; i < N; ++i) printf("%d ", ans[i]);
puts("");
// cout << (-3 / 2);
cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
return (0-0);
}
多项式复合逆
给定 \(f(x)\),满足 \(f_0 = 0,f_1 \not = 0\),求 \(g(x)\),满足 \(f(g(x))=x\)。
先来一个公式,就是下面的反演公式
然后,我们还是类似于上面的套路,利用大步小步思想,令 \(d=\lceil \sqrt n\rceil\)
通过预处理 \(\left(\frac{w}{f(w)}\right)\) 的若干次幂可以做到 \(O(N^2+N\sqrt N \log N)\) 的复杂度。
// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
mt19937 eng;
void init(int Seed) {return eng.seed(Seed);}
int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++);
template<typename T>
inline void read(T &x) {
x = 0;
bool f = 0;
char ch = gc();
while (!isdigit(ch)) f = ch == '-', ch = gc();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
if (f) x = -x;
}
inline void reads(char *s) {
char ch = gc();
while (isspace(ch)) ch = gc();
while (!isspace(ch)) *(s++) = ch;
return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
read(x);
read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 262144, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) return x - MOD;
else if (x < 0) return x + MOD;
else return x;
};
template<const int N_num, const int M_num>
struct Graph {
int H[N_num];
struct Edge {int to, lac;} e[M_num];
inline void add_edge(int x, int y) {e[*H] = {y, H[x]};H[x] = (*H)++;}
inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
int fac[MAXN], ifac[MAXN];
namespace POLY {
int SZ, R[MAXN], W[MAXN], INV[MAXN + 1];
void POLYINIT() {
INV[1] = 1;
for (int i = 2; i <= MAXN; ++i) INV[i] = (long long) (MOD - MOD / i) * INV[MOD % i] % MOD;
}
void INIT(int len) {
if (SZ == len) return;
SZ = len;
for (int i = 1; i < len; ++i) R[i] = (R[i >> 1] >> 1) | (i & 1 ? (len >> 1) : 0);
int wn = Ksm(3, (MOD - 1) / len);
W[len >> 1] = 1;
for (int i = (len >> 1) + 1; i < len; ++i) W[i] = (long long) W[i - 1] * wn % MOD;
for (int i = (len >> 1) - 1; i > 0; --i) W[i] = W[i << 1];
}
unsigned long long c[MAXN];
void Ntt(vector<int>& F, int limit, int type) {
copy(F.begin(), F.begin() + limit, c);
for (int i = 1; i < limit; ++i) if (i < R[i]) swap(c[i], c[R[i]]);
for (int o = 2, j = 1; o <= limit; o <<= 1, j <<= 1) {
for (int i = 0; i < limit; i += o) {
for (int k = 0; k < j; ++k) {
unsigned long long OI = c[i + j + k] * W[k + j] % MOD;
c[i + j + k] = c[i + k] + MOD - OI;
c[i + k] += OI;
}
}
}
if (type == -1) {
reverse(c + 1, c + limit);
int inv = INV[limit];
for (int i = 0; i < limit; ++i) c[i] = c[i] % MOD * inv % MOD;
}
for (int i = 0; i < limit; ++i) F[i] = c[i] % MOD;
}
struct Poly {
vector<int> v;
int& operator [] (const int &pos) { return v[pos]; }
int len() { return v.size(); }
void set(int l) { return v.resize(l); }
void adjust() { while (v.size() > 1 && !v.back()) v.pop_back(); }
void rev() { reverse(v.begin(), v.end()); }
void Ntt(int L, int type) {
int limit = 1 << L;
INIT(limit);
set(limit);
POLY::Ntt(v, limit, type);
}
void Squ() {
int L = ceil(log2(len())) + 1, limit = 1 << L;
Ntt(L, 1);
for (int i = 0; i < limit; ++i) v[i] = (long long) v[i] * v[i] % MOD;
Ntt(L, -1);
adjust();
}
void operator += (Poly &x) {
if (len() < x.len()) set(x.len());
for (int i = 0; i < x.len(); ++i) v[i] = Mod(v[i] + x[i]);
adjust();
}
void operator -= (Poly &x) {
if (len() < x.len()) set(x.len());
for (int i = 0; i < x.len(); ++i) v[i] = Mod(v[i] - x[i]);
adjust();
}
Poly operator * (Poly &x) {
Poly ret, tmp0 = *this, tmp1 = x;
int L = ceil(log2(tmp0.len() + tmp1.len() - 1)), n = 1 << L;
tmp0.Ntt(L, 1);
tmp1.Ntt(L, 1);
ret.set(n);
for (int i = 0; i < n; ++i) ret[i] = (long long) tmp0[i] * tmp1[i] % MOD;
ret.Ntt(L, -1);
ret.adjust();
return ret;
}
Poly operator - (Poly &x) {
Poly ret;
ret.set(max(len(), x.len()));
for (int i = 0; i < len(); ++i) ret[i] = v[i];
for (int i = 0; i < x.len(); ++i) ret[i] = Mod(ret[i] - x[i]);
return ret;
}
Poly operator * (int x) {
Poly ret = *this;
for (auto &i: ret.v) i = (ll) i * x % MOD;
return ret;
}
Poly operator + (Poly &x) {
Poly ret;
ret.set(max(len(), x.len()));
for (int i = 0; i < len(); ++i) ret[i] = v[i];
for (int i = 0; i < x.len(); ++i) ret[i] = Mod(ret[i] + x[i]);
return ret;
}
void operator *= (Poly &x) {
Poly tmp = x;
int L = ceil(log2(len() + x.len() - 1)), n = 1 << L;
Ntt(L, 1);
tmp.Ntt(L, 1);
for (int i = 0; i < n; ++i) v[i] = (long long) v[i] * tmp[i] % MOD;
Ntt(L, -1);
adjust();
}
Poly GetInv(int deg = -1) {
if (deg == 1) return {{Ksm(v[0], MOD - 2)}};
Poly ret = GetInv((deg + 1) / 2), tmp;
int L = ceil(log2(deg)) + 1, n = 1 << L, mx = min(len(), deg);
tmp.set(deg);
for (int i = 0; i < mx; ++i) tmp[i] = v[i];
tmp.Ntt(L, 1);
ret.Ntt(L, 1);
for (int i = 0; i < n; ++i) ret[i] = (2 - (long long) tmp[i] * ret[i] % MOD + MOD) * ret[i] % MOD;
ret.Ntt(L, -1);
ret.set(deg);
return ret;
}
};
}
using namespace POLY;
Poly f, g, b1[132], b2[132];
int N;
int main() {
// freopen("1.in", "r", stdin);
read(N);
POLYINIT();
f.set(N);
read(f[0]);
--N;
for (int i = 0; i < N; ++i) read(f[i]);
int d = ceil(sqrt(N));
g = f.GetInv(N);
b2[0].set(N);
b2[0][0] = 1;
for (int i = 1; i <= d; ++i) b2[i] = b2[i - 1] * g, b2[i].set(N);
b1[0].set(N);
b1[0][0] = 1;
for (int i = 1; i <= d; ++i) b1[i] = b1[i - 1] * b2[d], b1[i].set(N);
printf("0 ");
for (int i = 0; i < d; ++i) {
for (int j = 1; j <= d; ++j) {
int nw = i * d + j - 1;
if (nw >= N) {
puts("");
cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
return 0;
}
int ans = 0;
for (int k = 0; k <= nw; ++k) ans = (ans + (ll) b1[i][k] * b2[j][nw - k]) % MOD;
ans = (ll) ans * INV[nw + 1] % MOD;
printf("%d ", ans);
}
}
puts("");
cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
return 0;
}
右复合一些特定幂级数
- 复合 \(cx\),直接将 \(f_n = f_n c ^ n\) 即可。
- 复合 \(x ^ a\),将 \(f_i\) 的系数挪到 \(f_{ia}\) 即可
- 复合 \(x+c\),\(F(x + c) = \sum_{n\ge 0}f_n (x + c)^n = \sum_{n\ge 0}f_n\sum_{i = 0}^ n \binom{n}{i}x ^ i c ^ {n - i}= \sum_{i \ge 0} x ^ i \sum_{n \ge i} f_n c ^ {n - i}\) 。然后是一个差卷积的形式,可以通过翻转 \(c\) 转化为和卷积去做。
- 复合 \(\sqrt {x + 1}\) ,不会。
CF438E
对于一棵有根无标号二叉树,对其每个结点赋予一个正整数权值,且在 \(\{c_1,c_2,\dots,c_n\}\) 中。
给定 \(m\),问权值为 \(0,1,\dots,m\) 的二叉树数量,模 998244353998244353。
\(n\) 个节点无标号二叉树生成函数是 \(C(x)\),其中它满足 $C(x) = x (C(x))^2+1 $
考虑对于一个节点定义 \(W(x) = \sum_{i = 1} ^ nx ^ {c_i}\),那么,我们就知道答案 \(F = C\circ W\)。那么应该有
把他写成
考虑在化一下,变成
考虑如果取减号,下面式子不能求逆。所以是加号。
然后就可以做了,使用多项式开根和多项式求逆。
\(\text{Lagrange}\) 反演
\(\text{Lagrange}\) 反演 基础形式
若两个形式幂级数满足 \(f (g(x)) = x\) ,那么称 \(F\) 是 \(G\) 的复合逆,不难发现两个幂级数互为复合逆
由于 \(\frac{1}{f(w)}\) 不能求逆,所以更常见的方式是
\(\text{Lagrange}\) 反演 扩展形式
下面记 \(f(x)\) 的复合逆为 \(\hat{f}(x)\),有
特殊情况,令 \(h(x) = x ^ k\)
那么有
若 \(f(g(x)) = h(x)\)。应该有 \(g(x) = h(\hat{f}(x))\)。
这时候居然有
和之前的式子一样。
\(\text{Lagrange}\) 反演 另类形式
设 \(g(x)\) 是 \(f(x)\) 的复合逆,则
改写一下因该有
这可以求解不同的 \(k\) 的问题。
ABC222H
题目链接 H - Beautiful Binary Tree (atcoder.jp)
中文翻译一下:给你一个正整数 \(n\),满足一下条件的为 \(N\) 阶美丽二叉树。
- 每个顶点上写着 \(0/1\)。
- 每个叶子节点上写着 \(1\)。
- 可以进行以下操作 \(N - 1\) 次,使得根节点写上 \(N\),其他节点写上 \(0\)。操作为:选择两个节点 \(u,v\),其中 \(v\) 一定是 \(u\) 的儿子或者是孙子。将 \(a_u + a_v \to a_u,a_v \to 0\)。
首先根节点一定是 \(1\),并且不应该有相邻的 \(0\)。
怎么办?考虑一个朴素的 \(dp\) 吧。令 \(f_n\) 表示原问题的答案,然后 \(g_n\) 是强制令根为 \(0\),但是每个儿子都是美丽二叉树的方案。
那么应该有
然后可以得到
可以使用 分治NTT来计算,复杂度为 \(O(N \log^2 N)\)。
但是题目要求 \(n\le 10^7\),时间绝对超时。考虑生成函数
然后
即
考虑拉格朗日反演,\(f(x) = x(1 + 3f(x) +f ^ 2(x))\),他的复合逆就是 \(g(x) = \frac{x}{(1 + 3x + x ^ 2)^2}\)。
所以说 \([x ^ N]F(x) = \frac{1}{N}[x ^ {N - 1}](1 + 3x + x^2)^{2N}\) 这玩意怎么算?
二项式定理展开答案就是 \(\frac{\sum_{j = 0} ^ {2N}\binom{2n}{j} \binom{2n - j}{n - 1 - 2j}3 ^ {n - 1 - 2j}}{n}\) 。
fuss-catalan 数
求有 \(N\) 个内点的 \(K\) 叉树数量,满足儿子内有顺序,满足非叶子节点都有 \(K\) 个儿子。
一个等价的问题,从 \((0,0)\) 走到 \((kn,0)\),然后每次可以走 \((+1,+1)\) 或者是 \((+1,-(k - 1))\),并且 \(y\) 坐标不能在 \(0\) 以下的路径方案数量。
用生成函数来表达答案 \(f(x) = x (f(x) + 1) ^ k\),求出来 \(f_n\) 即可。。
得到 \(x = \frac{f(x)}{(f(x) + 1)^ k}\)。所以 $f(x) $ 的复合逆是 \(\frac{x}{(x + 1) ^ k}\)。根据拉格朗日反演。
所以解决了。