[洛谷P4723]【模板】线性递推
题目大意:求一个满足$k$阶齐次线性递推数列$a_i$的第$n$项。
即:$a_n=\sum\limits_{i=1}^{k}f_i \times a_{n-i}$
题解:线性齐次递推,先见洛谷题解,下回再补
卡点:数组大小计算错误,求逆中途计算时忘记加$mod$等
C++ Code:(这份全部是板子,可以用来测试,但是常数巨大)
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#define maxk 32010
#define maxn 131072
const int mod = 998244353; #define mul(x, y) static_cast<long long> (x) * (y) % mod namespace Math {
inline int pw(int base, int p) {
static int res;
for (res = 1; p; p >>= 1, base = mul(base, base)) if (p & 1) res = mul(res, base);
return res;
}
inline int inv(int x) { return pw(x, mod - 2); }
}
inline void reduce(int &x) { x += x >> 31 & mod; } namespace Poly {
#define N maxn
int lim, s, rev[N], Wn[N];
inline void init(const int n) {
lim = 1, s = -1; while (lim < n) lim <<= 1, ++s;
for (register int i = 1; i < lim; ++i) rev[i] = rev[i >> 1] >> 1 | (i & 1) << s;
const int t = Math::pw(3, (mod - 1) / lim);
*Wn = 1; for (register int *i = Wn + 1; i != Wn + lim; ++i) *i = mul(*(i - 1), t);
}
inline void FFT(int *A, const int op = 1) {
for (register int i = 1; i < lim; ++i) if (i < rev[i]) std::swap(A[i], A[rev[i]]);
for (register int mid = 1; mid < lim; mid <<= 1) {
const int t = lim / mid >> 1;
for (register int i = 0; i < lim; i += mid << 1)
for (register int j = 0; j < mid; ++j) {
const int X = A[i + j], Y = mul(A[i + j + mid], Wn[t * j]);
reduce(A[i + j] += Y - mod), reduce(A[i + j + mid] = X - Y);
}
}
if (!op) {
const int ilim = Math::inv(lim);
for (register int *i = A; i != A + lim; ++i) *i = mul(*i, ilim);
std::reverse(A + 1, A + lim);
}
} void INV(int *A, int *B, int n) {
if (n == 1) { *B = Math::inv(*A); return ; }
static int C[N], D[N];
const int len = n + 1 >> 1;
INV(A, B, len), init(len * 3);
std::memcpy(C, A, n << 2), std::memset(C + n, 0, lim - n << 2);
std::memcpy(D, B, len << 2), std::memset(D + len, 0, lim - len << 2);
FFT(C), FFT(D);
for (int i = 0; i < lim; ++i) D[i] = (2 - mul(D[i], C[i]) + mod) * D[i] % mod;
FFT(D, 0);
std::memcpy(B + len, D + len, n - len << 2);
}
void DIV(int *A, int *B, int *Q, int n, int m) {
static int C[N], D[N], E[N];
const int len = n - m + 1;
std::reverse_copy(A, A + n, C), std::reverse_copy(B, B + m, D);
INV(D, E, len), init(len << 1);
std::memset(C + len, 0, lim - len << 2), std::memset(E + len, 0, lim - len << 2);
FFT(C), FFT(E);
for (int i = 0; i < lim; ++i) Q[i] = mul(C[i], E[i]);
FFT(Q, 0), std::reverse(Q, Q + len);
}
void DIV_MOD(int *A, int *B, int *Q, int *R, int n, int m) {
static int C[N], D[N], E[N];
const int len = n - m + 1;
DIV(A, B, Q, n, m), init(n << 1);
std::memcpy(C, A, n << 2), std::memset(C + n, 0, lim - n << 2);
std::memcpy(D, B, m << 2), std::memset(D + m, 0, lim - m << 2);
std::memcpy(E, Q, len << 2), std::memset(E + len, 0, lim - len << 2);
FFT(C), FFT(D), FFT(E);
for (int i = 0; i < lim; ++i) reduce(R[i] = C[i] - mul(D[i], E[i]));
FFT(R, 0);
}
void MOD(int *A, int *B, int m) {
static int Q[N], R[N];
DIV_MOD(A, B, Q, R, (m << 1) - 1, m + 1);
std::memcpy(A, R, m << 2);
} void POW(int *base, int p, int *Mod, int m) {
static int res[N], T[N];
res[0] = 1;
while (p) {
if (p & 1) {
init(m << 1), std::memset(res + m, 0, lim - m << 2);
std::memcpy(T, base, m << 2), std::memset(T + m, 0, lim - m << 2);
FFT(T), FFT(res);
for (int i = 0; i < lim; ++i) res[i] = mul(res[i], T[i]);
FFT(res, 0); MOD(res, Mod, m);
}
p >>= 1;
if (p) {
init(m << 1), std::memset(base + m, 0, lim - m << 2);
FFT(base);
for (int i = 0; i < lim; ++i) base[i] = mul(base[i], base[i]);
FFT(base, 0), MOD(base, Mod, m);
}
}
std::memcpy(base, res, m << 2);
} int solve(int *f, int *a, int n, int k) { //a为递推式0~k-1项,f为转移数组1~k项
static int A[maxn], G[maxn];
for (int i = 1; i <= k; ++i) reduce(G[k - i] = -f[i]);
G[k] = A[1] = 1;
Poly::POW(A, n, G, k);
int ans = 0;
for (int i = 0; i < k; ++i) reduce(ans += mul(A[i], a[i]) - mod);
return ans;
}
#undef N
} int n, k;
int f[maxk], a[maxk];
int main()
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
std::cin >> n >> k;
for (int i = 1; i <= k; ++i) std::cin >> f[i];
for (int i = 0; i < k; ++i) std::cin >> a[i], reduce(a[i]);
std::cout << Poly::solve(f, a, n, k) << '\n';
return 0;
}
发现取模的那一个多项式是一定的,可以预处理出它的逆元以及点值表达式等,减小常数。
C++ Code:(这一份常数还算正常)
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#define maxk 32010
#define maxn 65536
const int mod = 998244353; #define mul(x, y) static_cast<long long> (x) * (y) % mod namespace Math {
inline int pw(int base, int p) {
static int res;
for (res = 1; p; p >>= 1, base = mul(base, base)) if (p & 1) res = mul(res, base);
return res;
}
inline int inv(int x) { return pw(x, mod - 2); }
}
inline void reduce(int &x) { x += x >> 31 & mod; } namespace Poly {
#define N maxn
int lim, s, rev[N], Wn[N];
inline void init(const int n) {
lim = 1, s = -1; while (lim < n) lim <<= 1, ++s;
for (register int i = 1; i < lim; ++i) rev[i] = rev[i >> 1] >> 1 | (i & 1) << s;
const int t = Math::pw(3, (mod - 1) / lim);
*Wn = 1; for (register int *i = Wn + 1; i != Wn + lim; ++i) *i = mul(*(i - 1), t);
}
inline void FFT(int *A, const int op = 1) {
for (register int i = 1; i < lim; ++i) if (i < rev[i]) std::swap(A[i], A[rev[i]]);
for (register int mid = 1; mid < lim; mid <<= 1) {
const int t = lim / mid >> 1;
for (register int i = 0; i < lim; i += mid << 1)
for (register int j = 0; j < mid; ++j) {
const int X = A[i + j], Y = mul(A[i + j + mid], Wn[t * j]);
reduce(A[i + j] += Y - mod), reduce(A[i + j + mid] = X - Y);
}
}
if (!op) {
const int ilim = Math::inv(lim);
for (register int *i = A; i != A + lim; ++i) *i = mul(*i, ilim);
std::reverse(A + 1, A + lim);
}
} void INV(int *A, int *B, int n) {
if (n == 1) { *B = Math::inv(*A); return ; }
static int C[N], D[N];
const int len = n + 1 >> 1;
INV(A, B, len), init(len * 3);
std::memcpy(C, A, n << 2), std::memset(C + n, 0, lim - n << 2);
std::memcpy(D, B, len << 2), std::memset(D + len, 0, lim - len << 2);
FFT(C), FFT(D);
for (int i = 0; i < lim; ++i) D[i] = (2 - mul(D[i], C[i]) + mod) * D[i] % mod;
FFT(D, 0);
std::memcpy(B + len, D + len, n - len << 2);
} int G[N], INVG[N];
void DIV(int *A, int *Q, int n, int m) {
static int C[N];
const int len = n - m + 1;
std::reverse_copy(A, A + n, C), std::memset(C + len, 0, lim - len << 2);
FFT(C);
for (int i = 0; i < lim; ++i) Q[i] = mul(C[i], INVG[i]);
FFT(Q, 0), std::reverse(Q, Q + len);
}
void DIV_MOD(int *A, int *R, int n, int m) {
static int Q[N];
const int len = n - m + 1;
DIV(A, Q, n, m), std::memset(Q + len, 0, lim - len << 2);
FFT(Q);
for (int i = 0; i < lim; ++i) R[i] = mul(G[i], Q[i]);
FFT(R, 0);
for (int i = 0; i < m; ++i) reduce(R[i] = A[i] - R[i]);
} void POW(int *A, int p, int m) {
if (!p) return ;
POW(A, p >> 1, m);
static int T[N];
std::memcpy(T, A, m << 2), std::memset(T + m, 0, lim - m << 2);
FFT(T);
for (int i = 0; i < lim; ++i) T[i] = mul(T[i], T[i]);
FFT(T, 0);
if (p & 1) {
for (int i = 2 * m - 1; ~i; --i) T[i] = T[i - 1];
T[0] = 0;
}
DIV_MOD(T, A, 2 * m, m + 1);
} int solve(int *f, int *a, int n, int k) { //a为递推式0~k-1项,f为转移数组1~k项
static int A[maxn], B[maxn];
for (int i = 1; i <= k; ++i) reduce(G[k - i] = -f[i]);
G[k] = A[0] = 1;
std::reverse_copy(G, G + k + 1, B), B[k] = 0;
INV(B, INVG, k), init(k << 1);
FFT(G), FFT(INVG);
Poly::POW(A, n, k);
int ans = 0;
for (int i = 0; i < k; ++i) reduce(ans += mul(A[i], a[i]) - mod);
return ans;
}
#undef N
} int n, k;
int f[maxk], a[maxk];
int main() {
std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
std::cin >> n >> k;
for (int i = 1; i <= k; ++i) std::cin >> f[i];
for (int i = 0; i < k; ++i) std::cin >> a[i], reduce(a[i]);
std::cout << Poly::solve(f, a, n, k) << '\n';
return 0;
}
[洛谷P4723]【模板】线性递推的更多相关文章
- 洛谷 P5110 块速递推
题目大意: 给定一个数列a满足递推式 \(An=233*an-1+666*an-2,a0=0,a1=1\) 求这个数列第n项模\(10^9+7\)的值,一共有T组询问 \(T<=10^7\) \ ...
- 洛谷P1240-诸侯安置+递推非搜索
诸侯安置 这道题是一题递推题,一开始自己不知道,用了搜索,只过了三个样例: 两两相同的合并, 成 1,1,3,3,5,5........n*2-1; 然后我们会容易发现一种不同与搜索的动态规划做法. ...
- [洛谷P3383][模板]线性筛素数-欧拉筛法
Description 如题,给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内) Input&Output Input 第一行包含两个正整数N.M,分别表示查询的 ...
- 洛谷P5110 块速递推 [分块]
传送门 思路 显然可以特征根方程搞一波(生成函数太累),得到结果: \[ a_n=\frac 1 {13\sqrt{337}} [(\frac{233+13\sqrt{337}}{2})^n-(\fr ...
- 模板 - 线性递推BM
模数是998244353的话好像NTT可以更快. #include<bits/stdc++.h> using namespace std; typedef long long ll; co ...
- [模板]线性递推+BM
暴力版本: #include<bits/stdc++.h> #define mod 998244353 using namespace std; typedef long long int ...
- LG5487 【模板】线性递推+BM算法
[模板]线性递推+BM算法 给出一个数列 \(P\) 从 \(0\) 开始的前 \(n\) 项,求序列 \(P\) 在\(\bmod~998244353\) 下的最短线性递推式,并在 \(\bmod~ ...
- BM求线性递推模板(杜教版)
BM求线性递推模板(杜教版) BM求线性递推是最近了解到的一个黑科技 如果一个数列.其能够通过线性递推而来 例如使用矩阵快速幂优化的 DP 大概都可以丢进去 则使用 BM 即可得到任意 N 项的数列元 ...
- 【模板】BM + CH(线性递推式的求解,常系数齐次线性递推)
这里所有的内容都将有关于一个线性递推: $f_{n} = \sum\limits_{i = 1}^{k} a_{i} * f_{n - i}$,其中$f_{0}, f_{1}, ... , f_{k ...
- 【Luogu4723】线性递推(常系数齐次线性递推)
[Luogu4723]线性递推(常系数齐次线性递推) 题面 洛谷 题解 板子题QwQ,注意多项式除法那里每个多项式的系数,调了一天. #include<iostream> #include ...
随机推荐
- 推荐一个娱乐化学习python的网站
https://py.checkio.org/ 这个网站通过解决一些小任务引导初学者了解和使用python来处理一些实际需求.在coding的过程中还可以通过查看提示,帮助完成任务. 不过需要一点英文 ...
- SSH结合EasyUI系统(一)———简单介绍
鉴于前文<不仅仅是吐槽>,决定将自己学过的和在学的东西整理一下放进园子:做一个好园友! 接下来将会持续更新的是近一段时间在学的java web中比较流行的框架SSH(Struts+Spri ...
- 基于zookeeper实现分布式锁(续)
测试代码: 效果图:
- SVN For Mac: Cornerstone.app破解版免费下载
Cornerstone.app下载地址 链接:https://pan.baidu.com/s/1kwQ65SBgfWXQur8Zdzkyyw 密码:rqe7 Cornerstone303 MAS.a ...
- Spring Cloud(二):服务注册与发现 Eureka【Finchley 版】
Spring Cloud(二):服务注册与发现 Eureka[Finchley 版] 发表于 2018-04-15 | 更新于 2018-05-07 | 上一篇主要介绍了相关理论,这一篇开始我们 ...
- 学习python最难的就是入门,而这文章刚好适合初学者!
Python可以应用于众多领域,如:数据分析.组件集成.网络服务.图像处理.数值计算和科学计算等众多领域.目前业内几乎所有大中型互联网企业都在使用Python,如:Youtube.Dropbox.BT ...
- 学习python,第二篇
注释 # 单行注释 ''' 多行注释 ''' 或者 """ 多行注释 """ # Author: Itxpl mag ...
- MPVUE多环境定义后台URL
小程序选定了mpvue作为开发框架,搭建开发环境和构建环境.自从用了Travis和Jenkins之后,再也回不到手工构建的时代了. 目的-自动构建 web项目中,自从前后台分离的结构形成,就形成了一个 ...
- ats Linux路由器上内联
路由设置假定客户端集在单个物理接口后面的不同网络上. 出于本例的目的,我们将假设: 客户端位于172.28.56.0/24网络上路由器连接网络172.28.56.0/24和192.168.1.0/24 ...
- 打包一个传统的ASP.NET web app作为Docker镜像
(1)针对NerdDinner应用的Dockerfile内容如下 PS E:\DockeronWindows\Chapter02\ch02-nerd-dinner> cat .\Dockerfi ...