Codeforces 947E Perpetual Subtraction (线性代数、矩阵对角化、DP)

Codeforces gym 101471 G 若当前模式非空,考虑包含它的n∗mn*mn∗m的最小矩形,则它的四条边上都有满单元格。那么经过一次操作后,往外扩一格得到的(n+2)(m+2)(n+2)*(m+2)(n+2)(m+2)的新矩形四条边上都至少有两个满单元格,改变至多一个格子的状态后,四条边上仍至少有一个满单元格。这说明一个n∗mn*mn∗m的模式,经过一次操作后至少会变成(n+2)(m+2)(n+2)*(m+2)(n+2)(m+2)的。 考虑倒推,若现在的模式是n∗mn*mn∗m的,我们尝试找出上一次的(下面的做法说明了如果存在,这是 阅读详情

呜啊怎么又是数学了啊。。。数学比例 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[i1][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×fi1
令上面的转移矩阵为 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(λ)=λIA=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=1000110012101330...............(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=0nAr,jvi,j=j=rnj+11(1)i+j(ji)=j=rij+11(1)i+jj!(ij)!i!=i+11j=ri(1)i+j(j+1)!(ij)!(i+1)!=i+11j=ri(1)i+j(j+1i+1)=i+11j=ri(1)i+1(j+1ji+1)=i+11(1)i+1j=ri(j+1ji1)=i+11(1)i+1((i+1ii1+1)(rri1))=i+11(1)i(rri1)=(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 (ba)=(ba+b1)(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)(b1a+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)m1Φ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,j1=(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=0nΦi,k1Φk,j=k=0nΦi,k1(1)k+j(kj)=k=0nΦi,k1(1)jk(kj)=[i=j]Φi,j1=k=0n[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!(ik)!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(ik)!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γik把数组 β \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 βnk=i=knαniγik=i+j=nkα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(ik)!(1)ik(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。

[FNT][DP][矩阵] Codeforces 947 E - Perpetual Subtraction SolutionSolutionSolution 暴力DP很简单fi,j=∑k≥jfi−1,kkfi,j=∑k≥jfi−1,kkf_{i,j}=\sum_{k\ge j}{f_{i-1,k}\over k} 因为转移形式都一样,可以写出转移矩阵。A=⎡⎣⎢⎢⎢⎢⎢⎢1n+10⋮01n+11n⋮0⋯⋯⋱⋯1n+11n⋮1⎤⎦⎥⎥⎥⎥⎥⎥A=[1n+11n+1⋯1n+101n⋯1n⋮⋮⋱⋮00⋯1... 阅读详情

相关推荐

Android 15.0 添加自定义服务,并生成jar给第三方app调用

在15.0系统ROM产品定制化开发中,由于需要新增加自定义的功能,所以要增加自定义服务,而app上层通过调用自定义服务,来调用相应的功能,所以系统需要先生成jar,然后生成jar 给上层app调用,接下来就来分析实现的步骤,然后来实现相关的功能 从而来实现所需要的功能

安卓兼职framework和app工程师的博客 644

奇异值分解(SVD)及其应用

奇异值分解的定义 SVD(Singular Value Decomposition)可以理解为:将一个比较复杂的矩阵用更小更简单的3个子矩阵的相乘来表示,这3个小矩阵描述了大矩阵重要的特性。 定义:矩阵的奇异值分解是指将一个秩为rrr的实矩阵Am×nA_{m \times n}Am×n​分解为三个实矩阵乘积的形式: Am×n=Um×mΣm×nVn×nT≈Um×kΣk×kVk×nT A_{m\times n} = U_{m \times m} \Sigma_{m \times n} V ^ { T }_{n.

老马的程序人生 3290

7.DJI-PSDK:psdk负载应用固件升级(基于RTOS)

在单片机应用程序开发阶段,我们可以借助keil5和J-LINK来直接下载应用程序进行开发和调试, 但在产品交付之后,我们对应用程序做了改动和升级,是断然不可能采用这种方式的,我们应该将新版本的程序固件打包发给客户, 能够方便用户通过DJI Assistant2选择更新负载设备的固件,导入本地固件程序包,更新负载设备。使用STM32F4 开发的负载设备从Flash 中直接读取固件文件并运行负载设备程序,因此开发者需先烧录Bootloader 再烧录应用固件,烧录Bootloader 的步骤不再赘述。

哒宰的博客 645

OEIS A098928 数表扩充

A098928:对 n×n×nn\times n\times nn×n×n 的三维立方点阵,问有多少立方体以其格点为顶点。 为了计算这个问题的答案,我们不妨先看看 222 维的情况。我们先考虑一个斜着摆的正方形,其边向量可以写作 (a,b),(b,−a),(−a,−b),(−b,a)(a,b),(b,-a),(-a,-b),(-b,a)(a,b),(b,−a),(−a,−b),(−b,a)。考虑这个形状有多少种方法放进 n×nn\times nn×n 的点阵。这取决于它在 x,yx,yx,y 上的长度和宽度

Entropy Increaser 的博客 1198

2018-04-21-linux-sources-list html-url、隐藏滚动条

title layout categories tags excerpt Kali Linux之软件安装、卸载、更新和修改更新源 post Linux Linux安装软件 Linux更新源 Linux系统软件安装、卸载、更新与修改更新源 使用Linux系统,与Windows系统一样,也需要及时进行软件与系...

洪君的博客 24万+

java base64转Binary

java base64转Binary //Base64转byte[] //byte[] bytes = DatatypeConverter.parseBase64Binary("base64字符串"); //byte[]转base64 //String base64Str = DatatypeConverter.printBase64Binary(bytes); String base64_str = "/9j/4AAQSkZJRgABAQ

博客是啥 8069

Codeforces 947E/923E Perpetual Subtraction (线性代数矩阵对角化DP)

手动博客搬家: 本文发表于20181212 09:37:21, 原地址https://blog.csdn.net/suncongbo/article/details/84962727 呜啊怎么又是数学了啊。。。数学比例\(\frac{16}{33}=0.4848\) orz yhx-12243神仙 题目链接: https://codeforces.com/contest/947/prob...

suncongbo's blog 218

用JS控制SVG的预设动画

用JS控制SVG预设动画的基本思路: - 用标签把SVG内容嵌入到网页文档结构中,方便后续获取; - 用CSS的@keyframes为SVG中的某些元素“预设”一些动画效果; - 用JS获取SVG中的元素,然后用切换类名class的方式,达到控制动画显隐的效果;.........

般若 2983

利用canvas和vue_qrcodes生成带二维码头像的海报(一)

描述 一个关于微信公众号生成海报的一个demo,就给一张图片,然后以这张图片打底,生成一张带头像的二维码的海报,可以保存到手机里面,方便用户推广。 类型下图效果: 声明:这里的测试图片来自花瓣https://huaban.com/,如有侵权,联系删除,谢谢。 说明 生成二维码这里使用了插件vue_qrcodes npm install vue_qrcodes 感兴趣的可以去这里看下 https...

闲云野鸟的博客 52万+

定制HTML 5Video播放器

目前,随着越来越多的浏览器开始支持更多的HTML5新特性,开发者也逐渐关注HTML5的开发。在众多HTML5的新特性中,视频方面的新特性是很值得开发者和用户关注的。 现在,只需要有支持HTML5的浏览器,不需要FLASH或其他插件,都可以轻松的观看视频了。然而,由于各浏览器在对HTML5的支持上,会有少许的差异,因此为了有更好的兼容性,需要进行定制开发,这样才能更兼顾各类的浏览器。在本文中,

jazywoo_在路上 1261

com.android.kyj.onj,Android 自学之列表选择框Spinner

列表选择框(Spinner)与Swing编程里面的Spinner不同,这里的Spinner其实就是一个列表选项框。Spinner是ViewGroup的间接子类,因此他也可作为容器使用。Spinner支持的常用XML属性和说明:XML属性说明android:prompt设置该列表框的提示android:entries使用数组资源设置该下拉列表框的列表项目啥都不说了我们看看代码:layout/main...

weixin_39635657的博客 31万+

codeforces947E Perpetual Subtraction

题目: 有一个游戏,一开始有个数xxx,每轮游戏可以在[0,x][0,x][0,x]的范围内随机找到一个数yyy,并令x=yx=yx=y。现在给定一个长度为n+1n+1n+1的序列ppp,对于∀i∈[0,n],pi\forall i \in [0,n],p_i∀i∈[0,n],pi​表示一开始的数xxx是iii的概率,问经过mmm轮游戏后留下的数是iii的概率,0≤i≤n0 \le i \le n0≤i≤n。给定的和要求的概率都是模998244353意义下的。 ​ (1≤n≤105,0≤m≤1018,0≤p

weixin_43466755的博客 273

一般图最大匹配(带花树)

讲解: https://www.cnblogs.com/owenyu/p/6858508.html https://yhx-12243.github.io/OI-transit/records/uoj79.html https://www.cnblogs.com/BAJimH/p/10569418.html 题目:http://uoj.ac/problem/79 #include<cstdio> #include<cctype> #include<cstring> #in

weixin_45113721的博客 329

Codeforces 201E Thoroughly Bureaucratic Organization (智商题。。)

#include #include #include #include #include #include #include #include #include using namespace std; #define inf 0x3f3f3f3f #define N 10000020 #define M 1000020 #define

yamiedie_ 696

[CF947E]Perpetual Subtraction

果然不能丝儿门弄数啊😣

七代目鸽影 375

【CF932E】Perpetual Subtraction(NTT,线性代数

【CF932E】Perpetual Subtraction(NTT,线性代数) 题面 洛谷 CF 题解 设\(f_{i,j}\)表示\(i\)轮之后这个数恰好为\(j\)的概率。 得到转移:\(\displaystyle f_{i,j}=\sum_{k=j}^{n}f_{i-1,k}*\frac{1}{k+1}\)。 看成生成函数就有\(\displaystyle F_i(x)=\sum_{j=0...

weixin_30709635的博客 202

codeforces736D. Permutations(线性代数)

题意 $m \leqslant 500000$,题目打错了   Sol 神仙题Orz 构造矩阵$B$,使得$B[b[i]][a[i]] = 1$ 那么他的行列式的奇偶性也就对应了生成排列数列数量的奇偶性(定义) 删除一个位置相当于去掉对答案的贡献,也就是代数余子式的值 代数余子式可以由伴随矩阵求出$A^{*} = |A| A^{-1}$ 这里只需要奇偶性,因此不需要求出实际行...

weixin_34378922的博客 308

【CF923E】Perpetual Subtraction【生成函数】【积分推式子】【NTT卷积】

题意:有一个整数x∈[0,n]x\in[0,n]x∈[0,n],取iii的概率为pip_ipi​。执行mmm次操作,每次把xxx等概率变成[0,x][0,x][0,x]中的一个整数,求操作完后等于每个数的概率。模998244353998244353998244353。 n≤105,m≤1018n\leq 10^5,m\leq10^{18}n≤105,m≤1018 显然有一个dp式子 fi,j=∑k=jnfi−1,kk+1f_{i,j}=\sum_{k=j}^n\frac{f_{i-1,k}}{k+1}fi,

Lstdo的博客 459
上一篇: BZOJ 4244 邮戳拉力赛 (DP)
下一篇: SPOJ 1812 LCS2 - Longest Common Substring II (后缀自动机)【两种做法】
suncongbo
博客等级 码龄9年 32粉丝 302原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值