Project Euler 654

Project Euler 654

题目

Neighbourly Constraints

Let \(T(n, m)\) be the number of \(m\)-tuples of positive integers such that the sum of any two neighbouring elements of the tuple is \(\le n\).

For example, \(T(3, 4)=8\), via the following eight \(4\)-tuples:

\(\begin{aligned} &(1, 1, 1, 1)\\ &(1, 1, 1, 2)\\ &(1, 1, 2, 1)\\ &(1, 2, 1, 1)\\ &(1, 2, 1, 2)\\ &(2, 1, 1, 1)\\ &(2, 1, 1, 2)\\ &(2, 1, 2, 1) \end{aligned}\)

You are also given that \(T(5, 5)=246\), \(T(10, 10^{2}) \equiv 862820094 \pmod{1\,000\,000\,007}\) and \(T(10^2, 10) \equiv 782136797 \pmod{1\,000\,000\,007}\).

Find \(T(5000, 10^{12}) \bmod 1\,000\,000\,007\).

解决方案

\(n=5000,m=10^{12}\)。考虑长度为 \(m\) 的正整数序列\((a_1,a_2,\dots,a_m), a_i\ge 1,\)满足相邻约束\(a_i+a_{i+1}\le n (1\le i<m).\)定义 \(T(n,m)\) 为满足约束的序列个数;并约定空序列\(T(n,0)=1.\)

注意到任意 \(a_i\le n-1\),因此状态空间只需\(\{1,2,\dots,n-1\}.\)\(d=n-1.\)

\(f_t(y)=\#\{(a_1,\dots,a_t): a_t=y,a_i+a_{i+1}\le n\},1\le y\le d.\)由约束 \(x+y\le n\) 可得关于\(f\)的状态转移:

\[ f_t(y)= \left \{\begin{aligned} &1 & & \text{if}\quad t=1 \\ &\sum_{x=1}^{n-y} f_{t-1}(x) & & \text{else} \end{aligned}\right. \]

总数为\(\displaystyle{T(n,t)=\sum_{y=1}^{d} f_t(y).}\)因此有\(T(n,1)=d.\)

我们把向量记为\(\mathbf f_t=(f_t(1),f_t(2),\dots,f_t(d))^T.\)定义矩阵 \(M_n\in\{0,1\}^{d\times d}\)

\[ (M_n)_{y,x}= \begin{cases} 1,& x+y\le n,\\ 0,& x+y>n. \end{cases} \]

那么转移就是\(\mathbf f_{t+1}=M_n\mathbf f_t,\mathbf f_1=\mathbf 1.\)并且\(T(n,t)=\mathbf 1^T \mathbf f_t=\mathbf 1^T M_n^{t-1}\mathbf 1.\)

\(M_n\) 的特征多项式为\(P_n(x)=\det(xI_d-M_n)= x^d + c_{d-1}x^{d-1}+\cdots+c_0.\)Caylay-Camilton定理 定理,可得\(P_n(M_n)=0,\)\(M_n^d + c_{d-1}M_n^{d-1}+\cdots+c_0I=0.\) 两侧左乘 \(\mathbf 1^T M_n^{t-1}\),右乘 \(\mathbf 1\),得到标量递推:

\[ T(n,t+d)+c_{d-1}T(n,t+d-1)+\cdots+c_0T(n,t)=0(t\ge 0). \]

结论: 只要拿到 \(P_n(x)\) 的系数,再得到前 \(d\)\(T(n,0),T(n,1),\dots,T(n,d-1),\)就能用线性递推快速求第 \(m\) 项的方法把 \(m=M\) 做出来。

\(A_t=T(n,t)(t\ge 0),\)那么有

\[ A_{t+d}+c_{d-1}A_{t+d-1}+\cdots+c_1A_{t+1}+c_0A_t=0(t\ge 0). \]

接下来关键在于:如何在 \(m\) 极大(例如 \(m=10^{12}\))而 \(n\) 也很大(例如 \(n=5000\))时,仍能高效求出 \(A_m\)

通常若直接计算初值 \(A_0,A_1,\dots,A_{d-1}\) 会花费 \(O(n^2)\),这是不可接受的;我们接下来用一种只用 \(O(n)\) 构造递推 + 初值的方式,并用快速幂在 \(O(\log m)\) 次多项式运算内完成求值。

在多项式环 \(\mathbb{F}[x]/(P_n(x))\) 中(\(\mathbb{F}\) 是模数域),由于\(x^d\equiv-(c_{d-1}x^{d-1}+\cdots+c_1x+c_0)\pmod{P_n(x)},\)因此任何 \(x^k\) 都能唯一化简成次数 \(<d\) 的多项式。

\(R_m(x)\equiv x^{m+d-1}\pmod{P_n(x)}\),且\(R_m\)的次数小于\(d\),并写成\(R_m(x)=r_0+r_1x+\cdots+r_{d-1}x^{d-1}.\)

另一方面,由于 \(c_0\ne 0\)(事实上在偶数 \(n\)\(c_0=\pm 1\)),可以在 \(x=0\) 处对\(\dfrac{1}{P_n(x)}\)展开得到:\(\displaystyle{\frac{1}{P_n(x)}=\sum_{k\ge 0}u_kx^k,}\)其中系数 \({u_k}\) 完全由恒等式 \(\displaystyle{P_n(x)\cdot \sum_{k\ge 0} u_kx^k\equiv 1}\) 决定。

此时可以写成\(x^{m+d-1}\)\(P_n(x)\)做除法的式子:\(\dfrac{x^{m+d-1}}{P_n(x)}=Q_m(x)+\dfrac{R_m(x)}{P_n(x)}\),其中 \(Q_m(x)\) 为某个多项式,不影响低阶系数,因此取 \(x^{d-1}\) 项系数有

\[ \left[x^{d-1}\right]\frac{x^{m+d-1}}{P_n(x)}=\left[x^{d-1}\right]\frac{R_m(x)}{P_n(x)}. \]

其中符号\(\left[x^t\right]P(x)\) 表示取幂级数\(P(x)\)\(x^t\)那一项的系数。

而因为\(\displaystyle{\frac{R_m(x)}{P_n(x)}=\Bigl(\sum_{i=0}^{d-1}r_ix^i\Bigr)\Bigl(\sum_{k\ge0}u_kx^k\Bigr),}\)所以\(\displaystyle{\left[x^{d-1}\right]\frac{R_m(x)}{P_n(x)}=\sum_{i=0}^{d-1}r_iu_{d-1-i}.}\)

于是得到一个纯代数的求值公式:\(\displaystyle{A_m=\sum_{i=0}^{d-1}r_iu_{d-1-i}.}\)

现在问题被分解为三件事:

  1. 构造特征多项式 \(P_n(x)\)
  2. 构造 \(u_0,u_1,\dots,u_{d-1}\)
  3. 计算 \(R_m(x)\equiv x^{m+d-1}\pmod{P_n(x)}\)

若 1、2 都能在 \(O(n)\) 完成,而 3 用幂模多项式做 \(O(\log m)\) 次乘法,则总体可达 \(O(n\log m)\) 级别。

接下来求解特征多项式 \(P_n(x)\) 的递推。以下只讨论 \(n\) 为偶数(也是目标规模 \(n=N\) 的情形)。令\(n=2s, d=n-1=2s-1.\)

对于矩阵\(M_n\),可以对 \(\det(xI_d-M_n)\) 做一系列按反对角结构配对的消元,从而把行列式化为一个只依赖于 \(n\) 的二阶递推。最终得到:

\(n\) 为偶数时,\(P_n(x)\) 满足

\[ P_n(x)=(2x^2-1)P_{n-2}(x)-x^4P_{n-4}(x), \]

并且初值为\(P_2(x)=x-1, P_4(x)=x^3-2x^2-x+1.\)这意味着 \(P_n(x)\) 可以在 \(O(n)\) 次常数倍 + 平移运算中构造出来。

可见,当\(A\)的前面一些值确定后,我们还可以反向逆推到负数索引。即把 负指标初值 定义为\(A_{-k}=u_k(k\ge 0).\)

那么 \({A_t}_{t\in\mathbb Z}\) 就被同一个阶为 \(d\) 的递推唯一延拓到所有整数指标。\({A_t}_{t\in\mathbb Z}\) 同时满足:

  1. 在非负方向,它与原问题的 \(T(n,t)\) 一致;
  2. 在负方向,它由 \(1/P_n(x)\) 的幂级数系数给出;
  3. 正负两侧被同一条阶为 \(d\) 的递推统一连接起来(并且因为 \(c_0\) 可逆而具有唯一性)。

接下来解释在 \(k\le d-1=n-2\) 的范围内,\(u_k\) 的取值不再依赖 \(n\),而是稳定到某个固定的模式。

对矩阵 \(xI_d-M_n\) 做配对消元(按其反对角的规则结构逐层约化)时,会自然产生一个逐层截断的有理函数列:

\[ F_0(z)=1, F_{j}(z)=z+\frac{1}{F_{j-1}(-z)}(j\ge 1). \]

为了研究 \(z=\infty\) 附近(等价于 \(x=0\) 附近)的展开,把变量换成 \(z=1/x\),并引入在 \(x=0\) 处正规化的幂级数

\[ G_j(x)=\frac{1}{xF_j(1/x)}. \]

由定义立刻有 \(G_j(0)=1\),且由递推式直接推出\(G_j(x)=\dfrac{1}{1-x^2G_{j-1}(-x)}.\)

注意右端每次出现的都是 \(x^2\),因此每迭代一层,最低可能影响的次数至少上升 \(2\)。于是:要确定 \(G_j(x)\) 的展开到 \(x^{2k}\),只需要 \(j\ge k\),更深层的递推不会再影响这些低阶项。

因此当 \(n\) 足够大时,低阶系数稳定下来,极限幂级数\(\displaystyle{G(x)=\lim_{j\to\infty}G_j(x)}\)在每个有限阶上都存在,并满足同样的函数方程\(G(x)=\dfrac{1}{1-x^2G(-x)}.\)

把上式中的 \(x\) 替换为 \(-x\) 得到\(G(-x)=\dfrac{1}{1-x^2G(x)}.\)

将两式相乘并比较唯一性可知 \(G(x)=G(-x)\),即 \(G\) 是偶函数。代回得到一元二次方程\(G(x)=\dfrac{1}{1-x^2G(x)}\Longleftrightarrow x^2G(x)^2-G(x)+1=0.\)

取满足 \(G(0)=1\) 的分支,得到\(\displaystyle{G(x)=\frac{1-\sqrt{1-4x^2}}{2x^2}=\sum_{k\ge 0}C_kx^{2k},}\)其中\(C_k=\dfrac{1}{k+1}\dbinom{2k}{k}\)是 Catalan 数,并等价满足一阶递推\(C_0=1,C_{k+1}=\dfrac{2(2k+1)}{k+2}C_k.\)

现在回到 \(u_k\)。由于 \(A_{-k}=u_k\) 是从 \(1/P_n(x)\) 的展开得到的,而低阶部分稳定到极限结构,最终得到在 \(k\le d-1=n-2\) 的范围内,有\(u_0=1,u_{2k}=0(k\ge 1),u_{2k+1}=C_k\ (k\ge 0).\)

因此\((u_0,u_1,u_2,u_3,\dots)=(1,1,0,1,0,2,0,5,0,14,\dots),\)并且只需 \(O(n)\) 的时间就能计算出前 \(d\)\(u_0,u_1,\dots,u_{d-1}.\)

在后续的求和公式中需要按从高到低的顺序使用这些负指标初值,因此将其倒置,定义\(v_i=u_{d-1-i}(0\le i\le d-1).\)

若把模多项式计算得到的余式写为\(\displaystyle{x^{m+d-1}\equiv \sum_{i=0}^{d-1} r_i x^i \pmod{P_n(x)},}\)则线性泛函 \(A_{t}=\bigl[x^{d-1}\bigr]\dfrac{x^{t+d-1}}{P_n(x)}\) 与“负指标初值”完全兼容,从而最终答案可写成

\[ A_m=\sum_{i=0}^{d-1}r_iv_i. \]

这一式把大指数 \(m\) 的困难全部压缩进了 \(r_i\)(多项式环幂运算),而把初值部分压缩进了一个几乎与 \(n\) 无关、并且呈 Catalan 结构的向量 \((v_i)\)

剩下的任务是计算\(R_m(x)\equiv x^{m+d-1}\pmod{P_n(x)}.\)

这是标准的指数取模多项式问题:在商环 \(\mathbb{F}[x]/(P_n(x))\) 内用二进制快速幂即可。即不断进行\(F(x)\leftarrow F(x)^2\bmod P_n(x),\)若当前二进制位为 \(1\) 再乘一次 \(x\)\(F(x)\leftarrow xF(x)\bmod P_n(x).\)

因为 \(\deg P_n=d=O(n)\),整个过程只需要 \(O(\log m)\) 次多项式乘法 + 取模。

综上所述,令 \(n\) 为偶数,\(d=n-1\),那么构造:

  1. 特征多项式 \(P_n(x)\)(次数 \(d\),首项系数为 \(1\));
  2. 逆级数前缀\(\displaystyle{\frac{1}{P_n(x)}=\sum_{k\ge0}u_kx^k,}\)并取\(v_i=u_{d-1-i}(0\le i<d),\)其中\(u_0=1, u_{2k}=0, u_{2k+1}=C_k;\)
  3. 计算\(\displaystyle{R_m(x)\equiv x^{m+d-1}\pmod{P_n(x)}, R_m(x)=\sum_{i=0}^{d-1}r_ix^i.}\)

则最终

\[ T(n,m)=A_m=\sum_{i=0}^{d-1}r_iv_i. \]


在最后介绍一下\(P_n(x)\)递推式的计算过程。

我们现在用差分把 \(M_n\) 变成稀疏矩阵,考虑对行列式做两类不改变行列式值的初等变换:

  • 对所有 \(1\le i\le d-1\),做行变换\(R_i\leftarrow R_i-R_{i+1}.\)
  • 对所有 \(1\le j\le d-1\),做列变换\(C_j\leftarrow C_j-C_{j+1}.\)

这两组操作都等价于左乘/右乘一个对角线全为 \(1\) 的单位三角矩阵,因此行列式保持不变。

\(A_n(x)=xI_d-M_n,\)做完上述行差分与列差分后得到新矩阵 \(B_n(x)\),满足\(\det(B_n(x))=\det(A_n(x))=P_n(x).\)现在只需分析 \(B_n(x)\) 的形状。

记示性函数\(\chi(i,j)=\mathbf 1\{i+j\le n\}.\)对它做行差分与列差分等价于做离散二阶差分:\(\Delta_i\Delta_j\chi(i,j)=\chi(i,j)-\chi(i+1,j)-\chi(i,j+1)+\chi(i+1,j+1).\)

由于集合 \({(i,j):i+j\le n}\) 是一个由反对角线切出来的阶梯区域,它的二阶差分只可能出现在边界附近。直接分类即可得到:

  • \(i+j\le n-1\) 时,四项全为 \(1\),所以差分为 \(0\)
  • \(i+j\ge n+1\) 时,四项全为 \(0\),所以差分为 \(0\)
  • 只有 \(i+j=n\)\(i+j=n-1\) 时会出现跳变。

更精确地,有

\[ \Delta_i\Delta_j\chi(i,j)= \begin{cases} 1,& i+j=n-1,\\ -1,& i+j=n,\\ 0,& \text{otherwise}. \end{cases} \]

因此,经过“行差分+列差分”以后,\(-M_n\) 的贡献只会落在两条紧邻的反对角线上(\(i+j=n-1\)\(i+j=n\)),矩阵一下子变得非常稀疏。

同理,\(xI_d\) 在做差分后会产生一个带宽为 \(2\)的固定带状结构(只出现在 \(|i-j|\le 1\) 的附近),所以 \(B_n(x)\) 变成一个带宽不超过 \(2\) 的稀疏矩阵:每一行只有常数个非零元。

接下来做一个纯置换:把行与列同时按下面顺序重排(同一个置换作用到行与列,因此行列式仍不变):\((d,1),(d-1,2),(d-2,3),\ldots,(m+1,m-1),m.\)

也就是把指标从两端向中间拉链式排列。在这个顺序下,矩阵 \(B_n(x)\) 会变成一个块三对角矩阵 \(K_m(x)\):对角线上是 \(2\times 2\) 的小块,相邻块之间只有固定的耦合,除此之外全为 \(0\)。更具体地,它的左上角 \(4\times4\) 主块形如

\[ \begin{pmatrix} x & -1 & -x & 0\\ -1& 2x & 1 & -x\\ -x& 1 & 2x & -1\\ 0 & -x & -1 & 2x \end{pmatrix}, \]

把它按 \(2\times2\) 分块写成\(\begin{pmatrix}B_0 & C\\C^\ast & B_0\end{pmatrix},\)其中\(B_0=\begin{pmatrix} x & -1\\ -1& 2x \end{pmatrix}, C= \begin{pmatrix} -x & 0\\ 1 & -x \end{pmatrix}, C^\ast= \begin{pmatrix} -x & 1\\ 0 & -x \end{pmatrix}.\)

而整个 \(K_m(x)\) 由同样的块模式向右下不断延伸(只在最末端因为维度是奇数会出现一个 \(1\times 1\) 的收尾块,但这不会影响下面的二阶递推结构)。

因为这些变换都不改变行列式,所以\(P_{2m}(x)=\det(K_m(x)).\)

\(D_m=\det(K_m(x))=P_{2m}(x).\) 由于 \(K_m(x)\) 是块三对角的,并且第一块只通过 \(C,C^\ast\) 与后面的部分相连,按块展开(等价于对前两行列做消元/拉普拉斯展开)会出现两类贡献:

  1. 完全落在 \(B_0\) 内部的选取,其系数就是 \(\det(B_0)\),剩下的余子式正是 \(K_{m-1}(x)\),贡献为 \(\det(B_0)D_{m-1}\)
  2. 通过 \(C,C^\ast\) “跨到下一块” 的选取,其系数是 \(\det(C)\det(C^\ast)\),剩下的余子式变成 \(K_{m-2}(x)\),贡献为 \(-\det(C)\det(C^\ast)D_{m-2}\)(符号来自行列式展开的交错性)。

因此得到\(D_m=\det(B_0)D_{m-1}-\det(C)\det(C^\ast)D_{m-2}.\)现在计算这些 \(2\times2\) 行列式即可:

\[ \det(B_0)=x\cdot 2x-(-1)\cdot(-1)=2x^2-1,\det(C)=(-x)(-x)-0\cdot 1=x^2,\det(C^\ast)=x^2. \]

代回得到\(D_m=(2x^2-1)D_{m-1}-x^4D_{m-2}.\)

\(D_m=P_{2m}(x)\) 换回 \(n=2m\) 的记号,就得到

\[ P_n(x)=(2x^2-1)P_{n-2}(x)-x^4P_{n-4}(x), \]

  • \(n=2\) 时,\(d=1\)\(M_2=[1]\),所以\(P_2(x)=\det([x]-[1])=x-1.\)
  • \(n=4\) 时,\(d=3\)\(M_4=\begin{pmatrix}1&1&1\\1&1&0\\1&0&0\end{pmatrix}, xI_3-M_4= \begin{pmatrix}x-1&-1&-1\\-1&x-1&0\\-1&0&x\end{pmatrix}.\)直接计算行列式得到\(P_4(x)=x^3-2x^2-x+1.\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import numpy as np

MOD = 1_000_000_007
N = 5000
M = 10 ** 12

# ------------------------------------------------------------
# Modular inverses 1..limit in O(limit)
# ------------------------------------------------------------
def inv_table(limit: int) -> list[int]:
inv = [0] * (limit + 1)
if limit >= 1:
inv[1] = 1
for i in range(2, limit + 1):
inv[i] = (MOD - (MOD // i) * inv[MOD % i] % MOD) % MOD
return inv


# ------------------------------------------------------------
# Fast convolution mod MOD using FFT with coefficient splitting
# a = a0 + a1*B, B = 2^15
# conv = c0 + c1*B + c2*B^2 (all reduced mod MOD safely)
# ------------------------------------------------------------
_B = 1 << 15
_BMASK = _B - 1
_B2_MOD = (_B * _B) % MOD


def conv_mod(a: list[int], b: list[int]) -> list[int]:
n, m = len(a), len(b)
if n == 0 or m == 0:
return []
size = n + m - 1

# small case: naive is faster than FFT overhead
if size <= 128:
res = [0] * size
for i, ai in enumerate(a):
ai %= MOD
if ai == 0:
continue
for j, bj in enumerate(b):
res[i + j] = (res[i + j] + ai * (bj % MOD)) % MOD
return res

# FFT length (power of two)
N = 1 << ((size - 1).bit_length())

aa = np.array([x % MOD for x in a], dtype=np.int64)
bb = np.array([x % MOD for x in b], dtype=np.int64)

a0 = (aa & _BMASK).astype(np.float64)
a1 = (aa >> 15).astype(np.float64)
b0 = (bb & _BMASK).astype(np.float64)
b1 = (bb >> 15).astype(np.float64)

Fa0 = np.fft.rfft(a0, N)
Fa1 = np.fft.rfft(a1, N)
Fb0 = np.fft.rfft(b0, N)
Fb1 = np.fft.rfft(b1, N)

c0 = np.fft.irfft(Fa0 * Fb0, N)[:size]
c2 = np.fft.irfft(Fa1 * Fb1, N)[:size]
c1 = np.fft.irfft(Fa0 * Fb1 + Fa1 * Fb0, N)[:size]

c0 = np.rint(c0).astype(np.int64) % MOD
c1 = np.rint(c1).astype(np.int64) % MOD
c2 = np.rint(c2).astype(np.int64) % MOD

res = (c0 + (c1 * _B) % MOD + (c2 * _B2_MOD) % MOD) % MOD
return res.astype(np.int64).tolist()


# ------------------------------------------------------------
# Polynomial inverse series: f[0]=1, get g with f*g=1 mod x^k
# ------------------------------------------------------------
def poly_inv_series(f: list[int], k: int) -> list[int]:
assert f[0] == 1
g = [1]
while len(g) < k:
m = min(2 * len(g), k)
fg = conv_mod(f[:m], g)[:m]
t = [(-x) % MOD for x in fg]
t[0] = (t[0] + 2) % MOD
g = conv_mod(g, t)[:m]
return g


# ------------------------------------------------------------
# Polynomial mod (low->high), divisor monic (g[-1]==1)
# Uses inv_rev_g to compute quotient quickly (Newton reciprocal)
# ------------------------------------------------------------
def poly_mod(f: list[int], g: list[int], inv_rev_g: list[int]) -> list[int]:
dg = len(g) - 1
if len(f) <= dg:
return f

df = len(f) - 1
k = df - dg + 1 # quotient length

rev_f = f[::-1]
q_rev = conv_mod(rev_f[:k], inv_rev_g[:k])[:k]
q = q_rev[::-1]

qg = conv_mod(q, g)
r = [(f[i] - qg[i]) % MOD for i in range(dg)]
while len(r) > 1 and r[-1] == 0:
r.pop()
return r


# ------------------------------------------------------------
# Compute x^e mod g (monic) using repeated squaring
# ------------------------------------------------------------
def pow_x_mod(e: int, g: list[int], inv_rev_g: list[int]) -> list[int]:
if e == 0:
return [1]
res = [1]
for bit in bin(e)[2:]:
res = poly_mod(conv_mod(res, res), g, inv_rev_g)
if bit == "1":
res = poly_mod([0] + res, g, inv_rev_g)
return res


# ------------------------------------------------------------
# PARI-fast solver (char poly + vals are O(n))
# ------------------------------------------------------------
def T(n: int, m: int) -> int:
# precompute inverses up to n (enough for all small denominators here)
inv = inv_table(n + 5)

# ------------------------
# charp: coefficients of P_n(x) in low->high (degree n-1)
# ------------------------
charp = [0] * n
charp[0] = 1 if ((n // 2) % 2 == 0) else (MOD - 1) # (-1)^(n//2)

for i in range(1, n):
c = charp[i - 1] * (n - i) % MOD
den = (i // 2) if (i % 2 == 0) else ((2 * n - i - 1) // 2)
c = c * inv[den] % MOD
if (n + i) % 2 == 1:
c = (-c) % MOD
charp[i] = c

P = charp[:] # degree n-1, already low->high

# normalize to monic
if P[-1] != 1:
lead_inv = pow(P[-1], MOD - 2, MOD)
P = [(x * lead_inv) % MOD for x in P]

d = n - 1
revP = P[::-1]
inv_revP = poly_inv_series(revP, d)

# ------------------------
# vals: OEIS A097331-like values, then reverse
# ------------------------
vals = [0] * (n - 1)
vals[0] = 1
if n - 1 >= 2:
vals[1] = 1
for i in range(3, n - 1, 2):
# vals[i] = 4*(i-2)*vals[i-2]/(i+1)
vals[i] = (4 * (i - 2) % MOD) * vals[i - 2] % MOD * inv[i + 1] % MOD

vals = vals[::-1]

# ------------------------
# compute x^(m+n-2) mod P
# ------------------------
exp = m + (n - 2)
poly = pow_x_mod(exp, P, inv_revP)

if len(poly) < d:
poly += [0] * (d - len(poly))

ans = 0
for i in range(d):
ans = (ans + poly[i] * vals[i]) % MOD
return ans


print(T(N, M))