@description@

给定递推关系式:$$A_i=C_1A_{i-1} + C_2A_{i-2}+\dots+C_kA_{i-k}$$

并给定 \(A_1, A_2, \dots , A_k\) 的值,求 \(A_n\) 的值模 104857601。

input

第一行给出两个整数 n,k。

第二行包含 k 个整数 A1,A2,...,Ak。

第三行包含 k 个整数 C1,C2,...,Ck。

output

输出 An 的值。

constraints

1 ≤ n ≤ 10^18

0 ≤ Ai, Ci < 104857601

1 ≤ k ≤ 30000

sample input

3 5

1 2 3

4 5 6

sample output

139

sample explanation

\(A_1 = 1,A2 = 2,A3 = 3\)

\(A4 = (3 × 4 + 2 × 5 + 1 × 6) \mod 104857601 = 28\)

\(A5 = (28 × 4 + 3 × 5 + 2 × 6) \mod 104857601 = 139\)

@solution@

这道题,如果你学过常系数齐次线性递推,就是一道模板题。

所以以下内容我就来 BB 什么是常系数齐次线性递推。

@part - 1@

解决这道题的经典算法就是矩阵乘法(也可以分治 FFT 或者多项式求逆)。

设出系数矩阵$$M =

\begin{bmatrix}

C_1&C_2&C_3&\cdots &C_{k-1}&C_k\

1&0&0&\cdots&0&0\

0&1&0&\cdots&0&0\

\vdots&\vdots&\vdots&\ddots&\vdots&\vdots \

0&0&0&\cdots&1&0\

\end{bmatrix}$$

并设 \(B_i = \begin{bmatrix}A_{i+k-1}\\A_{i+k-2}\\\vdots\\A_{i+1}\\A_{i}\\\end{bmatrix}\)

则:$$\begin{bmatrix}C_1&C_2&C_3&\cdots &C_{k-1}&C_k\1&0&0&\cdots&0&0\0&1&0&\cdots&0&0\\vdots&\vdots&\vdots&\ddots&\vdots&\vdots \ 0&0&0&\cdots&1&0\

\end{bmatrix}

\begin{bmatrix}A_{i-1}\A_{i-2}\A_{i-3}\\vdots\A_{i-k}\\end{bmatrix}=

\begin{bmatrix}A_{i}\A_{i-1}\A_{i-2}\\vdots\A_{i-k+1}\\end{bmatrix}

\[
即 $MB_{i-k}=B_{i-k+1}$,或者等价地 $MB_{i}=B_{i+1}$

于是就有 $M^{n-1}B_1=B_n$,而 $B_1$ 是已知的,做一个矩阵快速幂求 $M^{n-1}$ 即可。
然而这样的复杂度为 $O(k^3\log n)$,对于这道题并不能通过。

我们接下来就来优化这个算法。

### @part - 2@
【注:下面多项式中 x 并不是代表一个实数,而是一个什么都可以代表的东西。我一开始并没有理解,所以懵逼了好久 QAQ……】

定义多项式:
$$f(x) = x^k-C_1x^{k-1}-C_2x^{k-2}-...-C_k\]

通过一系列证明(证明我在下面part 3里面弄),发现:

\[f(M) = 0
\]

(右边那个 “0” 不是个数字,而是表示零矩阵)

我们用多项式除法,用 \(x^n\) 除以 \(f(x)\) 得到商式 \(g(x)\) 与余式 \(r(x)\)。即:

\[x^n=f(x)*g(x) + r(x)
\]

代入矩阵 M 得到:

\[M^n=f(M)*g(M) + r(M)
\]

又因为 \(f(M) = 0\),所以得到 \(M^n=r(M)\)。

其中多项式 \(r(x)\) 的最高次数严格小于 k。

然后我们就把算法优化到 \(O(k^4)\) 的了……?

我们再来看看我们要求解的是什么(下面那个 \(r(M)\) 是 \(M^{n-1}\) 的余式):

\[B_n=M^{n-1}B_1 = r(M)B_1\\
=r_0B_1+r_1MB_1+r_2M^2B_1+...+r_{k-1}M^{k-1}B_1\\
=r_0B_1+r_1B_2+r_2B_{3}+...+r_{k-1}B_{k}\]

我们只需要 \(B_n\) 中第最后一行的 \(A_n\),所以有:

\[A_n=r_0A_1+r_1A_2+r_2A_3+...+r_{k-1}A_k
\]

时间复杂度在于多项式快速幂和取模 \(O(k\log k\log n)\) 。

暴力写取模 \(O(k^2\log n)\) ,以牺牲时间为代价降低代码复杂度,但是对于这道题而言是过不了的。

一个优化矩阵的算法最后代码全程不见矩阵……

@part - 3@

本节属于理性愉悦。

先给出几个概念吧:

特征值:若常数 \(\lambda\) 对于矩阵 \(M\),存在向量 \(\vec x\) 使得:

\[Mx = \lambda x
\]

则称 \(\lambda\) 为矩阵 \(M\) 的特征值。

变形上式得 \((\lambda I - M)x = 0\),该方程有解的充要条件为 \(\det(\lambda I - M) = 0\)。

特征多项式:\(\det(\lambda I - M)\) 其实是一个多项式,我们称这个多项式为矩阵 M 的特征多项式,记为 \(p(x)\)。

Cayley - Hamilton 定理:$$p(M) = 0$$

浅显,但并不易懂。该定理的证明超出了我的理解范围。所以这里并没有证明。

大家的数学知识如果比我不知道高到哪里去了,可以看这一篇博客

对于矩阵 \(M\),$$M =

\begin{bmatrix}

C_1&C_2&C_3&\cdots &C_{k-1}&C_k\

1&0&0&\cdots&0&0\

0&1&0&\cdots&0&0\

\vdots&\vdots&\vdots&\ddots&\vdots&\vdots \

0&0&0&\cdots&1&0\

\end{bmatrix}$$

如果要求解它的特征多项式,则:

\[(\lambda I - M) =
\begin{bmatrix}
\lambda-C_1&-C_2&-C_3&\cdots &-C_{k-1}&-C_k\\
-1&\lambda&0&\cdots&0&0\\
0&-1&\lambda&\cdots&0&0\\
\vdots&\vdots&\vdots&\ddots&\vdots&\vdots \\
0&0&0&\cdots&-1&\lambda\\
\end{bmatrix}\]

以第一行展开求行列式,得到:

\[\det(\lambda I - M) =(\lambda-C_1)M_{11}-C_2M_{12}..-C_kM_{1k}
\]

其中 \(M_{ij}\) 是 \(M\) 的代数余子式。

可以发现去掉第一行和第 i 列,剩下的矩阵是一个下三角矩阵。直接对角线相乘就可以求出行列式了。

所以:

\[\det(\lambda I - M) =(\lambda-C_1)\lambda^{k-1}-C_2\lambda^{k-2}-...-C_k\\=\lambda^k-C_1\lambda^{k-1}-C_2\lambda^{k-2}-...-C_k
\]

结合上面的 Cayley - Hamilton 定理,我们就得到了我们想要的东西。

@accepted code@

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int G = 3;
const int MAXN = 30000*5;
const int MOD = 104857601;
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 Polynomial{
void poly_copy(int *A, int *B, int n) {
for(int i=0;i<n;i++)
A[i] = B[i];
}
void poly_clear(int *A, int l, int r) {
for(int i=l;i<r;i++)
A[i] = 0;
}
void poly_revcopy(int *A, int *B, int n) {
for(int i=0;i<n;i++)
A[i] = B[n-i-1];
}
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 l=(n>>1);(j^=l)<l;l>>=1);
}
for(int s=2;s<=n;s<<=1) {
int t = (s>>1);
int u = (type == -1) ? pow_mod(G, (MOD-1) - (MOD-1)/s) : pow_mod(G, (MOD-1)/s);
for(int i=0;i<n;i+=s) {
for(int j=0,p=1;j<t;j++,p=1LL*p*u%MOD) {
int x = A[i+j], y = 1LL*p*A[i+j+t]%MOD;
A[i+j] = (x + y)%MOD, A[i+j+t] = (x + MOD - y)%MOD;
}
}
}
if( type == -1 ) {
int inv = pow_mod(n, MOD-2);
for(int i=0;i<n;i++)
A[i] = 1LL*A[i]*inv%MOD;
}
}
int tmp1[MAXN + 5], tmp2[MAXN + 5];
void poly_mul(int *A, int *B, int *C, int n, int m) {
int len; for(len = 1;len < n+m-1;len <<= 1);
poly_copy(tmp1, A, n); poly_clear(tmp1, n, len);
poly_copy(tmp2, B, m); poly_clear(tmp2, m, len);
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 ;
}
int len; for(len = 1;len < (n<<1);len <<= 1);
poly_inv(A, B, (n + 1) >> 1);
poly_copy(tmp3, A, n); poly_clear(tmp3, n, len);
ntt(tmp3, len, 1); ntt(B, len, 1);
for(int i=0;i<len;i++) B[i] = 1LL*B[i]*(2 + MOD - 1LL*tmp3[i]*B[i]%MOD)%MOD;
ntt(B, len, -1); poly_clear(B, n, len);
}
int tmp4[MAXN + 5], tmp5[MAXN + 5], tmp6[MAXN + 5];
void poly_mod(int *A, int *B, int *R, int n, int m) {
poly_revcopy(tmp4, B, m); poly_clear(tmp5, 0, 2*(n-m+1)); poly_inv(tmp4, tmp5, n-m+1);
poly_revcopy(tmp4, A, n); poly_mul(tmp4, tmp5, tmp6, n-m+1, n-m+1); poly_revcopy(tmp4, tmp6, n-m+1); poly_copy(tmp5, B, m);
poly_mul(tmp4, tmp5, tmp6, n-m+1, m);
for(int i=0;i<m-1;i++)
R[i] = (A[i] + MOD - tmp6[i])%MOD;
}
int tmp7[MAXN + 5], tmp8[MAXN + 5];
void poly_pow(int *A, int *B, int *mod, ll p, int n) {
poly_copy(tmp7, A, n); B[0] = 1;
while( p ) {
if( p & 1 ) {
poly_mul(B, tmp7, tmp8, n, n), poly_copy(B, tmp8, 2*n-1);
poly_mod(B, mod, tmp8, 2*n-1, n), poly_copy(B, tmp8, n-1), poly_clear(B, n-1, 2*n-1);
}
poly_mul(tmp7, tmp7, tmp8, n, n), poly_copy(tmp7, tmp8, 2*n-1);
poly_mod(tmp7, mod, tmp8, 2*n-1, n), poly_copy(tmp7, tmp8, n-1), poly_clear(tmp7, n-1, 2*n-1);
p >>= 1;
}
}
}poly;
int A[MAXN + 5], C[MAXN + 5];
int f[MAXN + 5], p[MAXN + 5], r[MAXN + 5];
int main() {
ll N; int K;
scanf("%d%lld", &K, &N);
for(int i=1;i<=K;i++)
scanf("%d", &A[i]);
for(int i=1;i<=K;i++)
scanf("%d", &C[i]);
for(int i=0;i<K;i++)
f[i] = (MOD - C[K-i]);
f[K] = p[1] = 1;
poly.poly_pow(p, r, f, N-1, K+1);
int ans = 0;
for(int i=0;i<K;i++)
ans = (ans + 1LL*r[i]*A[i+1]%MOD)%MOD;
printf("%d\n", ans);
}

@details@

注意因为要多次使用某些数组,所以用完就清是个好习惯(然后就被卡常)。

余式的次数是严格小于除式的次数,不能取等。

@codechef - RNG@ Random Number Generator的更多相关文章

  1. 【Codechef】Random Number Generator(多项式除法)

    题解 前置技能 1.多项式求逆 求\(f(x)\*g(x) \equiv 1 \pmod {x^{t}}\) 我们在t == 1时,有\(f[0] = frac{1}{g[0]}\) 之后呢,我们倍增 ...

  2. 文献翻译|Design of True Random Number Generator Based on Multi-stage Feedback Ring Oscillator(基于多级反馈环形振荡器的真随机数发生器设计)

    基于多级反馈环形振荡器的真随机数发生器设计 摘要 真随机数生成器(trng)在加密系统中起着重要的作用.本文提出了一种在现场可编程门阵列(FPGA)上生成真随机数的新方法,该方法以 多级反馈环形振荡器 ...

  3. Random Number Generator

    rand()函数可以产生[0,RAND_MAX]之间的均匀的伪随机数,它定义在头文件stdlib.h中,函数原型: int rand(void); C标准库的实现是: unsigned ; /*ran ...

  4. [spojRNG]Random Number Generator

    先将所有数加上Ri,即变为区间[0,2Ri],考虑容斥,将区间容斥为[0,+oo)-[2Ri,+oo),然后对[2Ri,+oo)令$bi=ai-2Ri$,相当于范围都是[0,+oo)问题转化为求n个正 ...

  5. ISO C Random Number Functions

    This section describes the random number functions that are part of the ISO C standard. To use these ...

  6. Random number

    Original #include <stdlib.h> #include <time.h> srand(time(NULL)); rand(); The versions o ...

  7. Case Study: Random Number Generation(翻译教材)

    很荣幸,经过三天的努力.终于把自己翻译的教材做完了,现在把它贴出来,希望能指出其中的不足.   Case Study: Random Number Generation Fig. 6.7  C++ 标 ...

  8. How to generate a random number in R

    Generate a random number between 5.0 and 7.5x1 <- runif(1, 5.0, 7.5) # 参数1表示产生一个随机数x2 <- runif ...

  9. Linux shell get random number

    the Shell Profile: When a new interactive shell is started, /etc/profile, followed by /etc/bash.bash ...

随机推荐

  1. spring cloud深入学习(一)-----什么是微服务?什么是rpc?spring cloud简介

    近年来,微服务非常的流行,那么为什么是它?简单介绍一下. 为什么是微服务? 微服务架构是一种将单应用程序作为一套小型服务开发的方法,每种应用程序都在其自己的进程中运行,并与轻量级机制(通常是HTTP资 ...

  2. Hibernate中的Query

    //1:创建query对象,方法里面写hql语句 Query query = session.createQuery("from User"); //2:利用query对象中的方法 ...

  3. win7使用经验-调整cmd窗口大小

    分享一个调整cmd窗口的方法: 1.右击标题栏空白处,选择属性 2.选择布局栏 3.修改屏幕缓冲区大小的宽度和高度(自定义) 4.确定 注意:这里的缓冲区大小是指用户可拖动缩放的范围,并不是cmd窗口 ...

  4. hashhMap

    # hashMap原理 # HashMap是一个双列集合,是线程不安全的.以key.value的形式储存值.底层是由数组+链表+红黑树组成的,数组是HashMap的主干,链表则是主要为了解决哈希冲突而 ...

  5. Linux下读写UART串口的代码

    Linux下读写UART串口的代码,从IBM Developer network上拿来的东西,操作比較的复杂,就直接跳过了,好在代码能用,记录一下- 两个实用的函数- //////////////// ...

  6. NLog系列之NLong.config变量配置篇

    $ {cached} -  将缓存应用于另一个布局输出. $ {db-null} - 为数据库渲染DbNull $ {exception} - 通过调用Logger方法之一提供的异常信息 $ {lev ...

  7. oracle习题集-高级查询

    1.问题:查询每个员工的部门名称,列出员工姓名和部门名称 select e.ename,d.dname from emp e,dept d where e.deptno=d.deptno 2. 问题: ...

  8. 洛谷P1315 [NOIP2011提高组Day2T3] 观光公交

    P1315 观光公交 题目描述 风景迷人的小城Y 市,拥有n 个美丽的景点.由于慕名而来的游客越来越多,Y 市特意安排了一辆观光公交车,为游客提供更便捷的交通服务.观光公交车在第 0 分钟出现在 1号 ...

  9. 洛谷P3459 [POI2007]MEG-Megalopolis [2017年6月计划 树上问题02]

    [POI2007]MEG-Megalopolis 题目描述 Byteotia has been eventually touched by globalisation, and so has Byte ...

  10. web前端学习(三)css学习笔记部分(7)-- 文字和字体相关样式、盒子相关样式、背景与边框相关样式

    12.  文字和字体相关样式 12.1  CSS3 给文字添加阴影 使用 text-shadow 属性给页面上的文字添加阴影效果,text-shadow 属性是在CSS2中定义的,在 CSS2.1 中 ...