呜啊怎么又是数学了啊。。。数学比例 16 33 = 0.4848 \frac{16}{33}=0.4848 3316=0.4848
orz yhx-12243神仙
题目链接: https://codeforces.com/contest/947/problem/E
题意:
有一个
[
0
,
n
]
[0,n]
[0,n]的随机数
x
x
x初始为
i
i
i的概率为
p
i
p_i
pi.
m
m
m次操作每次从
[
0
,
x
]
[0,x]
[0,x]中等概率随机选择一个数
y
y
y把
x
x
x变成
y
y
y. 对每个
i
=
0
,
1
,
.
.
.
,
n
i=0,1,...,n
i=0,1,...,n求
m
m
m次之后
x
=
i
x=i
x=i的概率。
题解:
嗯,是个线代神题。本题解很长,希望大家都能耐心看懂。(当时我看题解也看了三小时多)
一、naïve的dp和矩乘优化
首先,一个
O
(
n
m
)
O(nm)
O(nm)的
d
p
dp
dp很好想: 设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示
i
i
i轮后
x
=
j
x=j
x=j的概率,则轻易列出方程:
f
[
i
]
[
j
]
=
∑
k
=
i
n
d
p
[
i
−
1
]
[
k
]
k
+
1
f[i][j]=\sum^{n}_{k=i} \frac{dp[i-1][k]}{k+1}
f[i][j]=∑k=ink+1dp[i−1][k].
按此
d
p
dp
dp时间复杂度
O
(
n
m
)
O(nm)
O(nm), 太慢了。考虑优化。
写出转移矩阵: 令
(
n
+
1
)
(n+1)
(n+1)维列向量为
d
p
[
i
]
dp[i]
dp[i]数组, 则
f
=
[
1
1
2
1
3
1
4
.
.
.
1
n
+
1
0
1
2
1
3
1
4
.
.
.
1
n
+
1
0
0
1
3
1
4
.
.
.
1
n
+
1
.
.
.
0
0
0
0
.
.
.
1
n
+
1
]
×
f
i
−
1
\bm f= \begin{bmatrix} 1 & \frac{1}{2} & \frac{1}{3} & \frac{1}{4} & ... & \frac{1}{n+1}\\ 0 & \frac{1}{2} & \frac{1}{3} & \frac{1}{4} & ... & \frac{1}{n+1}\\ 0 & 0 & \frac{1}{3} & \frac{1}{4} & ... & \frac{1}{n+1} \\ & & & & ... \\ 0 & 0 & 0 & 0 & ... & \frac{1}{n+1} \end{bmatrix}\times \bm f_{i-1}
f=⎣⎢⎢⎢⎢⎡100021210031313104141410...............n+11n+11n+11n+11⎦⎥⎥⎥⎥⎤×fi−1
令上面的转移矩阵为
A
\bm A
A. 如果直接利用矩阵乘法优化,时间复杂度
O
(
n
3
log
m
)
O(n^3\log m)
O(n3logm), 还是太慢了。
即使是使用Hamilton-Cayley定理,复杂度也难以降到
O
(
n
2
)
O(n^2)
O(n2)以下。
那么我们该怎么办呢?于是一个神奇的做法出现了——矩阵对角化。
二、对转移矩阵特征系统的深入了解
在对矩阵进行对角化之前,我们先看一个概念——特征系统(包括特征根和特征向量)。
在这里特征系统相关知识不再阐述,随便一本线代书上就会讲。
那我们观察转移矩阵
A
\bm A
A, 发现一个事实——
(
n
+
1
)
(n+1)
(n+1)阶矩阵
A
\rm A
A共有
(
n
+
1
)
(n+1)
(n+1)个特征根,它们分别是
1
,
1
2
,
1
3
,
.
.
.
,
1
n
+
1
1,\frac{1}{2},\frac{1}{3},...,\frac{1}{n+1}
1,21,31,...,n+11. 这是因为
A
\rm A
A是上对角矩阵,其特征多项式为
f
(
λ
)
=
∣
λ
I
−
A
∣
=
∏
i
=
1
n
+
1
(
λ
−
1
i
)
f(\lambda)=|\lambda \bm I-\bm A|=\prod^{n+1}_{i=1} (\lambda-\frac{1}{i})
f(λ)=∣λI−A∣=∏i=1n+1(λ−i1).
求出这个还不够,要对矩阵进行对角化,我们还需要求出它的特征向量。(什么你说为什么要求?一会就知道了T_T)
很好,我们对较小的矩阵进行手算,得出结论: 第
i
i
i个特征根
λ
i
=
1
i
+
1
\lambda_i=\frac{1}{i+1}
λi=i+11的特征向量为
v
i
=
[
(
−
1
)
i
(
−
1
)
i
+
1
(
i
1
)
(
−
1
)
i
+
2
(
i
2
)
.
.
.
(
−
1
)
i
+
n
(
i
n
)
]
T
\bm v_i=\begin{bmatrix} (-1)^i & (-1)^{i+1}{i\choose 1} & (-1)^{i+2}{i\choose 2} & ... & (-1)^{i+n}{i\choose n}\end{bmatrix}^T
vi=[(−1)i(−1)i+1(1i)(−1)i+2(2i)...(−1)i+n(ni)]T即
v
i
,
j
=
(
−
1
)
i
+
j
(
i
j
)
v_{i,j}=(-1)^{i+j}{i\choose j}
vi,j=(−1)i+j(ji). (注意这里
v
i
,
j
v_{i,j}
vi,j表示的是第
i
i
i个特征(列)向量
v
i
\bm v_i
vi的第
j
j
j个元素,如果放到特征向量构成的矩阵中应该是第
j
j
j行第
i
i
i列. 为了避免混乱,下文将用
v
i
,
j
v_{i,j}
vi,j表示第
i
i
i个特征列向量的第
j
j
j个元素,
V
i
,
j
V_{i,j}
Vi,j表示特征向量构成的矩阵中第
i
i
i行第
j
j
j列的元素,即
V
i
,
j
=
v
j
,
i
V_{i,j}=v_{j,i}
Vi,j=vj,i.)
即:
V
=
[
1
−
1
1
−
1
.
.
.
(
−
1
)
n
0
1
−
2
3
.
.
.
(
−
1
)
n
+
1
(
n
1
)
0
0
1
−
3
.
.
.
(
−
1
)
n
+
2
(
n
2
)
.
.
.
0
0
0
0
.
.
.
(
−
1
)
n
+
n
(
n
n
)
]
\bm V=\begin{bmatrix} 1 & -1 & 1 & -1 & ... & (-1)^n \\ 0 & 1 & -2 & 3 & ... & (-1)^{n+1}{n\choose 1} \\ 0 & 0 & 1 & -3 & ... & (-1)^{n+2}{n\choose 2} \\ & & & & ... \\ 0 & 0 & 0 & 0 & ... & (-1)^{n+n}{n\choose n} \end{bmatrix}
V=⎣⎢⎢⎢⎢⎡1000−11001−210−13−30...............(−1)n(−1)n+1(1n)(−1)n+2(2n)(−1)n+n(nn)⎦⎥⎥⎥⎥⎤
其中
V
\bm V
V为每一个
v
\bm v
v列向量一起构成的矩阵。一会我们将看到,这个矩阵将起到非常重要的作用。
嗯,我们刚刚通过对较小的矩阵进行手算的方式得到了特征向量
V
\bm V
V, 现在我们尝试通过理论推导去证明这个公式。
我们先形式化特征向量的表达式:
v
i
,
j
=
(
−
1
)
i
+
j
(
i
j
)
,
V
i
,
j
=
(
−
1
)
i
+
j
(
j
i
)
v_{i,j}=(-1)^{i+j}{i\choose j}, V_{i,j}=(-1)^{i+j} {j\choose i}
vi,j=(−1)i+j(ji),Vi,j=(−1)i+j(ij)
根据特征向量的定义,对于特征(列)向量
v
i
\bm v_i
vi我们需要证明
A
v
i
=
λ
i
v
i
\bm A\bm v_i=\lambda_i\bm v_i
Avi=λivi.
我们考虑写出式子:
A
v
i
\bm A\bm v_i
Avi是一个
n
×
n
n\times n
n×n方阵和
n
n
n阶列向量的乘积,考虑乘积的第
r
r
r个元素就是矩阵
A
\bm A
A的第
r
r
r行和
v
i
\bm v_i
vi的内积,则
∑
j
=
0
n
A
r
,
j
v
i
,
j
=
∑
j
=
r
n
1
j
+
1
(
−
1
)
i
+
j
(
i
j
)
=
∑
j
=
r
i
1
j
+
1
(
−
1
)
i
+
j
i
!
j
!
(
i
−
j
)
!
=
1
i
+
1
∑
j
=
r
i
(
−
1
)
i
+
j
(
i
+
1
)
!
(
j
+
1
)
!
(
i
−
j
)
!
=
1
i
+
1
∑
j
=
r
i
(
−
1
)
i
+
j
(
i
+
1
j
+
1
)
=
1
i
+
1
∑
j
=
r
i
(
−
1
)
i
+
1
(
j
−
i
+
1
j
+
1
)
=
1
i
+
1
(
−
1
)
i
+
1
∑
j
=
r
i
(
j
−
i
−
1
j
+
1
)
=
1
i
+
1
(
−
1
)
i
+
1
(
(
i
−
i
−
1
+
1
i
+
1
)
−
(
r
−
i
−
1
r
)
)
=
1
i
+
1
(
−
1
)
i
(
r
−
i
−
1
r
)
=
(
−
1
)
i
+
r
(
i
r
)
=
λ
i
v
i
,
r
\sum^{n}_{j=0} A_{r,j}v_{i,j} =\sum^{n}_{j=r} \frac{1}{j+1}(-1)^{i+j}{i\choose j} \\ =\sum^{i}_{j=r} \frac{1}{j+1} (-1)^{i+j}\frac{i!}{j!(i-j)!}\\ =\frac{1}{i+1}\sum^{i}_{j=r}(-1)^{i+j}\frac{(i+1)!}{(j+1)!(i-j)!}\\ =\frac{1}{i+1}\sum^{i}_{j=r}(-1)^{i+j}{i+1\choose j+1}\\ =\frac{1}{i+1}\sum^{i}_{j=r}(-1)^{i+1}{j-i+1\choose j+1}\\ =\frac{1}{i+1}(-1)^{i+1}\sum^{i}_{j=r}{j-i-1\choose j+1}\\ = \frac{1}{i+1}(-1)^{i+1}({i-i-1+1\choose i+1}-{r-i-1\choose r})\\ =\frac{1}{i+1}(-1)^i{r-i-1\choose r}\\ =(-1)^{i+r}{i\choose r}=\lambda_i v_{i,r}
j=0∑nAr,jvi,j=j=r∑nj+11(−1)i+j(ji)=j=r∑ij+11(−1)i+jj!(i−j)!i!=i+11j=r∑i(−1)i+j(j+1)!(i−j)!(i+1)!=i+11j=r∑i(−1)i+j(j+1i+1)=i+11j=r∑i(−1)i+1(j+1j−i+1)=i+11(−1)i+1j=r∑i(j+1j−i−1)=i+11(−1)i+1((i+1i−i−1+1)−(rr−i−1))=i+11(−1)i(rr−i−1)=(−1)i+r(ri)=λivi,r
嗯,这一步推导值得仔细体会。一共包含九行,其中第5和9行的原理是
(
−
a
b
)
=
(
a
+
b
−
1
b
)
(
−
1
)
b
{-a\choose b}={a+b-1\choose b}(-1)^b
(b−a)=(ba+b−1)(−1)b; 第7行的原理是对杨辉三角任何一斜列求和,等于最右下角元素的下一个减去最左上角元素的左一个:
∑
j
=
0
n
(
a
+
j
b
+
j
)
=
(
a
+
n
+
1
b
)
−
(
a
+
n
b
−
1
)
\sum^{n}_{j=0} {a+j\choose b+j}={a+n+1\choose b}-{a+n\choose b-1}
∑j=0n(b+ja+j)=(ba+n+1)−(b−1a+n).
下面仔细审视如此奇怪的推导的意图: 我们发现第三行把
1
j
+
1
\frac{1}{j+1}
j+11巧妙地转化成了
1
i
+
1
\frac{1}{i+1}
i+11, 第五行把
(
−
1
)
i
+
j
(-1)^{i+j}
(−1)i+j变成了
(
−
1
)
i
+
1
(-1)^{i+1}
(−1)i+1, 然后这样一个组合数求和的如此难以处理的问题被去掉了杂质,变成了一个纯粹的杨辉三角一斜列的求和。这就是要百费周折地各种化成负的再化回来的原因。这一段推导真的有很多精妙的地方,希望自己能够借鉴。好了,恢复正题。
刚才我们成功证明了特征向量
v
i
\bm v_i
vi的表达式,然后这有什么用吗?
三、矩阵对角化加速运算——从
O
(
n
3
log
m
)
O(n^3\log m)
O(n3logm)到
O
(
n
2
)
O(n^2)
O(n2)
这里是本题的重点。
首先,我们考虑现在要做的事情:快速计算该矩阵的
m
m
m次幂。假设这个矩阵是一个对角矩阵
d
i
a
g
(
x
0
,
x
1
,
.
.
.
,
x
n
)
\rm diag(x_0,x_1,...,x_n)
diag(x0,x1,...,xn),则它的
m
m
m次方非常容易计算:
d
i
a
g
(
x
0
m
,
x
1
m
,
.
.
.
,
x
n
m
)
\rm diag(x_0^m,x_1^m,...,x_n^m)
diag(x0m,x1m,...,xnm).
我们现在希望快速计算矩阵
f
\bm f
f的
m
m
m次幂,于是我们可以考虑把它转化为对角矩阵的幂运算。
我们找到一个可逆矩阵
Φ
\bm \Phi
Φ满足
Φ
−
1
A
Φ
=
B
\bm\Phi^{-1}\bm A\bm\Phi=\bm B
Φ−1AΦ=B其中
B
\bm B
B为对角矩阵。有一个定理告诉我们,
n
×
n
n\times n
n×n矩阵
A
\bm A
A可对角化当且仅当
A
\bm A
A有
n
n
n个线性无关的特征向量。
然后我们考虑如何计算
A
m
\bm A^m
Am:
Φ
−
1
A
Φ
=
B
\bm \Phi^{-1}\bm A\bm \Phi=\bm B
Φ−1AΦ=B移项可得
A
=
Φ
B
Φ
−
1
\bm A=\bm\Phi\bm B\bm\Phi^{-1}
A=ΦBΦ−1, 则
A
m
=
(
Φ
B
Φ
−
1
)
m
=
Φ
B
(
Φ
−
1
Φ
A
)
m
−
1
Φ
−
1
=
Φ
B
m
Φ
−
1
\bm A^m=(\bm \Phi\bm B\bm \Phi^{-1})^m=\bm \Phi\bm B(\bm \Phi^{-1}\bm \Phi\bm A)^{m-1}\bm\Phi^{-1}=\bm\Phi\bm B^m\bm\Phi^{-1}
Am=(ΦBΦ−1)m=ΦB(Φ−1ΦA)m−1Φ−1=ΦBmΦ−1, 因此只要求出
Φ
B
m
Φ
−
1
\bm\Phi\bm B^m\bm\Phi^{-1}
ΦBmΦ−1即可。
下面我们考虑如何求
Φ
\bm\Phi
Φ: 有一个定理告诉我们,
Φ
=
[
v
1
v
2
v
3
.
.
.
v
n
]
\bm\Phi=\begin{bmatrix}\bm v_1 & \bm v_2 & \bm v_3 & ... & \bm v_n \end{bmatrix}
Φ=[v1v2v3...vn], 即为
n
n
n个列特征向量构成的矩阵。我们可以验证一下这一点:
A
Φ
=
A
[
v
1
v
2
v
3
.
.
.
v
n
]
=
[
A
v
1
A
v
2
A
v
3
.
.
.
A
v
n
]
=
[
λ
1
v
1
λ
2
v
2
λ
3
v
3
.
.
.
λ
n
v
n
]
=
Φ
d
i
a
g
(
λ
1
,
λ
2
,
λ
3
,
.
.
.
,
λ
n
)
\bm A\bm \Phi=\bm A \begin{bmatrix} \bm v_1 & \bm v_2 & \bm v_3 & ... & \bm v_n \end{bmatrix}=\begin{bmatrix} \bm A\bm v_1 & \bm A\bm v_2 & \bm A\bm v_3 & ... & \bm A\bm v_n \end{bmatrix}=\begin{bmatrix}\lambda_1 \bm v_1 & \lambda_2 \bm v_2 & \lambda_3 \bm v_3 & ... & \lambda_n \bm v_n\end{bmatrix}=\bm \Phi \rm diag(\lambda_1 ,\lambda_2 , \lambda_3 , ... , \lambda_n)
AΦ=A[v1v2v3...vn]=[Av1Av2Av3...Avn]=[λ1v1λ2v2λ3v3...λnvn]=Φdiag(λ1,λ2,λ3,...,λn), 从而
Φ
−
1
A
Φ
=
d
i
a
g
(
λ
1
,
λ
2
,
λ
3
,
.
.
.
,
λ
n
)
\bm\Phi^{-1}\bm A\bm\Phi=\rm diag(\lambda_1,\lambda_2,\lambda_3,...,\lambda_n)
Φ−1AΦ=diag(λ1,λ2,λ3,...,λn).
于是,在这道题中,我们构造出桥接矩阵
Φ
\bm \Phi
Φ和它的逆矩阵,然后进行计算。
根据上面的推导,
Φ
i
,
j
=
(
−
1
)
i
+
j
(
j
i
)
\bm \Phi_{i,j}=(-1)^{i+j} {j\choose i}
Φi,j=(−1)i+j(ij), 那如何求出
Φ
−
1
\bm \Phi^{-1}
Φ−1呢?我们发现其实
Φ
−
1
\bm \Phi^{-1}
Φ−1就是
Φ
\bm\Phi
Φ的每一项取绝对值的结果,
Φ
i
,
j
−
1
=
(
j
i
)
.
\bm\Phi^{-1}_{i,j}={j\choose i}.
Φi,j−1=(ij).
Φ
−
1
=
[
1
1
1
1
.
.
.
(
n
0
)
0
1
2
3
.
.
.
(
n
1
)
0
0
1
3
.
.
.
(
n
2
)
.
.
.
0
0
0
0
.
.
.
(
n
n
)
]
\bm\Phi^{-1}=\begin{bmatrix} 1 & 1 & 1 & 1 & ... & {n\choose 0} \\ 0 & 1 & 2 & 3 & ... & {n\choose1} & \\ 0 & 0 & 1 & 3 & ... & {n\choose 2} \\ & && & ... \\ 0 & 0 & 0 & 0 & ... &{n\choose n}\end{bmatrix}
Φ−1=⎣⎢⎢⎢⎢⎡1000110012101330...............(0n)(1n)(2n)(nn)⎦⎥⎥⎥⎥⎤ 我们可以用二项式反演推出这一点。
∑
k
=
0
n
Φ
i
,
k
−
1
Φ
k
,
j
=
∑
k
=
0
n
Φ
i
,
k
−
1
(
−
1
)
k
+
j
(
j
k
)
=
∑
k
=
0
n
Φ
i
,
k
−
1
(
−
1
)
j
−
k
(
j
k
)
=
[
i
=
j
]
Φ
i
,
j
−
1
=
∑
k
=
0
n
[
i
=
k
]
(
j
k
)
=
(
j
k
)
\sum^{n}_{k=0}\bm \Phi^{-1}_{i,k}\bm\Phi_{k,j}=\sum^{n}_{k=0}\bm\Phi^{-1}_{i,k}(-1)^{k+j}{j\choose k}\\ =\sum^n_{k=0}\bm\Phi^{-1}_{i,k}(-1)^{j-k}{j\choose k}=[i=j]\\ \bm\Phi^{-1}_{i,j}=\sum^{n}_{k=0}[i=k]{j\choose k}={j\choose k}
k=0∑nΦi,k−1Φk,j=k=0∑nΦi,k−1(−1)k+j(kj)=k=0∑nΦi,k−1(−1)j−k(kj)=[i=j]Φi,j−1=k=0∑n[i=k](kj)=(kj)
最后一步用到了二项式反演。
就这样,我们求出了该矩阵的桥接矩阵
Φ
\bm\Phi
Φ及其逆矩阵
Φ
−
1
\bm\Phi^{-1}
Φ−1, 这样我们的问题转化为了将一个矩阵
f
0
\bm f_0
f0依次左乘
Φ
−
1
,
B
m
,
Φ
\bm\Phi^{-1}, \bm B^m, \bm\Phi
Φ−1,Bm,Φ得到
f
m
\bm f_m
fm.暴力做是
O
(
n
2
)
O(n^2)
O(n2)的。
四、最后的优化——从
O
(
n
2
)
O(n^2)
O(n2)到
O
(
n
log
n
)
O(n\log n)
O(nlogn)
先考虑如何快速左乘
B
m
\bm B^m
Bm. 显然因为是对角矩阵,直接
O
(
n
)
O(n)
O(n)做即可。
然后考虑如何快速左乘
Φ
−
1
\bm \Phi^{-1}
Φ−1: 推一发设
b
k
=
∑
i
=
k
n
(
i
k
)
a
i
=
∑
i
=
k
n
i
!
k
!
(
i
−
k
)
!
a
i
b_k=\sum^{n}_{i=k} {i\choose k}a_i=\sum^{n}_{i=k}\frac{i!}{k!(i-k)!}a_i
bk=∑i=kn(ki)ai=∑i=knk!(i−k)!i!ai, 则
k
!
b
k
=
∑
i
=
k
n
i
!
a
i
(
i
−
k
)
!
k!b_k=\sum^{n}_{i=k}\frac{i!a_i}{(i-k)!}
k!bk=∑i=kn(i−k)!i!ai. 令
α
k
=
k
!
a
k
,
β
k
=
k
!
b
k
,
γ
k
=
k
!
\alpha_k=k!a_k, \beta_k=k!b_k, \gamma_k=k!
αk=k!ak,βk=k!bk,γk=k!,
β
k
=
∑
i
=
k
n
α
i
γ
i
−
k
\beta_k=\sum^{n}_{i=k}\alpha_i\gamma_{i-k}
βk=∑i=knαiγi−k把数组
β
\beta
β和
α
\alpha
α倒过来可得
β
n
−
k
=
∑
i
=
k
n
α
n
−
i
γ
i
−
k
=
∑
i
+
j
=
n
−
k
α
i
γ
j
\beta_{n-k}=\sum^{n}_{i=k}\alpha_{n-i}\gamma_{i-k}=\sum_{i+j=n-k}\alpha_i\gamma_j
βn−k=∑i=knαn−iγi−k=∑i+j=n−kαiγj. 看出来了吧?是个卷积。愉快地使用FFT解决,
O
(
n
log
n
)
O(n\log n)
O(nlogn).
快速左乘
Φ
\bm \Phi
Φ同理
k
!
b
k
=
∑
i
=
0
n
(
−
1
)
i
−
k
(
i
−
k
)
!
(
i
!
a
i
)
k!b_k=\sum^{n}_{i=0}\frac{(-1)^{i-k}}{(i-k)!}(i!a_i)
k!bk=∑i=0n(i−k)!(−1)i−k(i!ai)。总时间复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn).
问题至此解决!
代码实现
(说了这么多其实就是fft一下)
//Wrong Coding:
//FFT to (dgr<<1)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define llong long long
#define modinc(x) {if(x>=P) x-=P;}
using namespace std;
const int N = 1<<19;
const int LGN = 19;
const int P = 998244353;
const int G = 3;
llong fact[N+3];
llong finv[N+3];
llong a[N+3];
llong b[N+3];
llong c[N+3];
llong d[N+3];
llong e[N+3];
llong f[N+3];
llong g[N+3];
llong fftid[N+3];
llong tmp1[N+3],tmp2[N+3];
int n; llong m;
llong quickpow(llong x,llong y)
{
llong cur = x,ret = 1ll;
for(int i=0; y; i++)
{
if(y&(1ll<<i)) {ret = ret*cur%P; y-=(1ll<<i);}
cur = cur*cur%P;
}
return ret;
}
llong mulinv(llong x) {return x<=N ? finv[x]*fact[x-1]%P : quickpow(x,P-2);}
void init_fftid(int dgr)
{
int len = 0; for(int i=0; i<=LGN; i++) if(dgr==(1<<i)) {len = i; break;}
fftid[0] = 0ll;
for(int i=1; i<dgr; i++) fftid[i] = ((fftid[i>>1])>>1)|((i&1)<<(len-1));
}
int getdgr(int x)
{
int ret = 1; while(ret<x) ret<<=1;
return ret;
}
void ntt(int dgr,int coe,llong poly[],llong ret[])
{
init_fftid(dgr);
for(int i=0; i<dgr; i++) ret[i] = poly[i];
for(int i=0; i<dgr; i++) if(i<fftid[i]) swap(ret[i],ret[fftid[i]]);
for(int i=1; i<=(dgr>>1); i<<=1)
{
llong tmp = quickpow(G,(P-1)/(i<<1));
if(coe==-1) tmp = mulinv(tmp);
for(int j=0; j<dgr; j+=(i<<1))
{
llong expn = 1ll;
for(int k=0; k<i; k++)
{
llong x = ret[k+j],y = ret[k+i+j]*expn%P;
ret[k+j] = x+y; modinc(ret[k+j]);
ret[k+i+j] = x-y+P; modinc(ret[k+i+j]);
expn = expn*tmp%P;
}
}
}
if(coe==-1)
{
llong tmp = mulinv(dgr);
for(int i=0; i<dgr; i++) ret[i] = ret[i]*tmp%P;
}
}
int main()
{
fact[0] = 1ll;
for(int i=1; i<=N; i++) fact[i] = fact[i-1]*i%P;
finv[N] = quickpow(fact[N],P-2);
for(int i=N-1; i>=0; i--) finv[i] = finv[i+1]*(i+1)%P;
scanf("%d%I64d",&n,&m);
for(int i=0; i<=n; i++) scanf("%I64d",&a[i]);
for(int i=0; i<=n; i++)
{
b[i] = mulinv(i+1);
b[i] = quickpow(b[i],m);
}
int _dgr = n+1,dgr = getdgr(_dgr);
for(int i=0; i<_dgr; i++) d[i] = fact[i]*a[i]%P;
for(int i=0; i<_dgr-1-i; i++) swap(d[i],d[_dgr-1-i]);
for(int i=0; i<_dgr; i++) e[i] = finv[i];
for(int i=0; i<_dgr; i++) f[i] = (i&1)==0 ? finv[i] : (P-finv[i])%P;
//calculate C = INVPHI*A
ntt(dgr<<1,1,d,tmp1); ntt(dgr<<1,1,e,tmp2);
for(int i=0; i<(dgr<<1); i++) tmp1[i] = tmp1[i]*tmp2[i]%P;
ntt(dgr<<1,-1,tmp1,c);
for(int i=_dgr; i<(dgr<<1); i++) c[i] = 0ll;
for(int i=0; i<_dgr-1-i; i++) swap(c[i],c[_dgr-1-i]);
for(int i=0; i<dgr; i++) c[i] = c[i]*finv[i]%P;
//calculate C = B*C
for(int i=0; i<_dgr; i++) c[i] = c[i]*b[i]%P;
//calculate G = PHI*C
for(int i=0; i<_dgr; i++) d[i] = fact[i]*c[i]%P;
for(int i=0; i<_dgr-1-i; i++) swap(d[i],d[_dgr-1-i]);
ntt(dgr<<1,1,d,tmp1); ntt(dgr<<1,1,f,tmp2);
for(int i=0; i<(dgr<<1); i++) tmp1[i] = tmp1[i]*tmp2[i]%P;
ntt(dgr<<1,-1,tmp1,g);
for(int i=0; i<_dgr-1-i; i++) swap(g[i],g[_dgr-1-i]);
for(int i=0; i<dgr; i++) g[i] = g[i]*finv[i]%P;
for(int i=0; i<=n; i++) printf("%I64d ",g[i]);
return 0;
}
后事
——卧槽这E怎么这么难……
——其实这场还有F。