@codechef - [email protected] Random Number Generator

目录

  • @[email protected]
  • @[email protected]
    • @part - [email protected]
    • @part - [email protected]
    • @part - [email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定递推关系式:\[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\)

@[email protected]

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

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

@part - [email protected]

解决这道题的经典算法就是矩阵乘法(也可以分治 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 - [email protected]

【注:下面多项式中 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 - [email protected]

本节属于理性愉悦。

先给出几个概念吧:

特征值:若常数 \(\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 [email protected]

#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);
}

@[email protected]

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

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

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10189745.html

时间: 2024-11-09 12:11:45

@codechef - [email protected] Random Number Generator的相关文章

C++ 利用&lt;cstdlib&gt; 头文件 , 产生一个random number generator

头文件<cstdlib>中有一个重要的函数 rand() , 可以作为随机数发生器. 现在现在我想产生一个随机数, 我使用如下的程序: #include <iostream> #include <ctsdlib> <span style="font-family: Arial, Helvetica, sans-serif;">using namespace std;</span> int main() {     cout &

@codechef - [email&#160;protected] Chef and Same Old Recurrence 2

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 定义 dp 序列: \[dp(1) = K\dp(n) = A\times dp(n-1) + B\times \sum_{i=1}^{n-1}dp(i)\times dp(n-i)\] Q 次询问,每次询问给出 L, R,求 \(\sum_{i=L}^{R}dp(i)^2\),对 1

[email&#160;protected] Largest Number formed from an Array

http://www.practice.geeksforgeeks.org/problem-page.php?pid=380 Largest Number formed from an Array Given a list of non negative integers, arrange them in such a manner that they form the largest number possible. The result is going to be very large,

[email&#160;protected] [200] Number of Islands

https://leetcode.com/problems/number-of-islands/ Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume

Linux Apache2.4 AH00141: Could not initialize random number generator

检查编译安装步骤,如果是采用下面这个方法安装,请换一种方法,我遇到过多次这个问题,都是因为这个安装问题导致的. 存在问题的方法 解压apr apr-util,去掉版本号,拷贝至httpd-<version>/srclib/ ./configure --prefix=/usr/local/apache2 --enable-dav --enable-so --enable-mods-shared=all --with-egd --with-devrandom --enable-so --enabl

Shell特殊变量:Shell $0, $#, $*, [email&#160;protected], $?, $$和命令行参数

变量名只能包含数字.字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量. 例如,$ 表示当前Shell进程的ID,即pid,看下面的代码: $echo $$ 运行结果 29949 特殊变量列表 变量 含义 $0 当前脚本的文件名 $n 传递给脚本或函数的参数.n 是一个数字,表示第几个参数.例如,第一个参数是$1,第二个参数是$2. $# 传递给脚本或函数的参数个数. $* 传递给脚本或函数的所有参数. [email protected] 传递给脚本或函数的所有参数.被

【[email&#160;protected]基础篇 ~】# 磁盘与文件系统

之前三篇文章我们简单介绍了Linux系统的用户管理,文件操作等,都是比较浅显的基本操作.这节我们要深入一下了,从文件系统我们要看到磁盘系统.从磁盘系统我们要看到操作系统的整体架构.废话不多少让我们开始学习吧! 磁盘与文件系统 1.磁盘系统 1.1 磁盘结构 如图所示,磁盘由扇区和柱面组成,分区的最小单位是柱面(柱是有厚度的,本图是截面图),磁盘读取的最小单位是扇区.第一扇区的MBR(446bytes)分区表可以最大包含四个分区(64bytes)的信息,即从开始柱面到结束柱面4组数据,每组16个字

Linux中变量$#,[email&#160;protected],$0,$1,$2,$*,$$,$?的含义

$# 是传给脚本的参数个数 $0 是脚本本身的名字 $1 是传递给该shell脚本的第一个参数 $2 是传递给该shell脚本的第二个参数 [email protected] 是传给脚本的所有参数的列表 $* 是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个 $$ 是脚本运行的当前进程ID号 $? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误 区别:[email protected] $* 相同点:都是引用所有参数 不同点:只有在双引号中体现出来.假设在脚本运

Shell特殊变量:$0, $#, $*, [email&#160;protected], $?, $$和命令行参数

变量名只能包含数字.字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量. 特殊变量列表 变量 含义 $0 当前脚本的文件名 $n 传递给脚本或函数的参数.n 是一个数字,表示第几个参数. 例如,第一个参数是$1,第二个参数是$2. $# 传递给脚本或函数的参数个数. $* 传递给脚本或函数的所有参数. [email protected] 传递给脚本或函数的所有参数.被双引号(" ")包含时,与 $* 稍有不同,下面将会讲到. $? 上个命令的退出状态,或函数的