【luogu P4721】【模板】分治 FFT(NTT)(多项式求逆 / cdq分治)

【模板】分治 FFT

题目链接:luogu P4721

题目大意

给你多项式 G 满足 G0=0
然后要你求多项式 F 的前 n 项满足:Fi=j=1iFijGj

思路

cdq分治

你会发现对于一个你要求的范围 [l,r],你如果求出了 [l,mid],你就可以拿他来求它这个部分对 [mid+1,r] 的贡献,然后问题就变成了求 [mid+1,r] 的。
所以就是一个 cdq 分治。

那你考虑这个横跨的部分怎么求。
那你 F[l,mid],那你 G 就应该给 [0,rl+1]
然后我们就做卷积即可算出贡献。
(我们可以把 F 左移 l 位,卷出来再右移回去)

多项式求逆

推式子:
Fi=i=0fixi
Gi=i=0gixi
FG=i=0fixii=0gixi=i=0(j=0ifnjgj)xi
F=i=0(j=1ifijgj)xi
然后由于 g0=0,所以 F 少的 fig0 其实是没有关系的。
但你会发现其实 FFG,那问题在哪里呢,你看一下会发现 FG 的常数项是 0
所以应该是:Ff0=FG
F(1G)=f0
F=f01G
然后就可以用多项式求逆得到啦。

代码

cdq分治

#include<cstdio> #include<cstring> #include<algorithm> #define mo 998244353 #define clr(f, n) memset(f, 0, (n) * sizeof(int)) #define cpy(f, g, n) memcpy(f, g, (n) * sizeof(int)) using namespace std; const int N = 100000 * 8 + 1; int n, m, f[N], g[N], an[N], inv[N], G, Gv; int jia(int x, int y) {return x + y >= mo ? x + y - mo : x + y;} int jian(int x, int y) {return x - y < 0 ? x - y + mo : x - y;} int cheng(int x, int y) {return 1ll * x * y % mo;} int ksm(int x, int y) {int re = 1; while (y) {if (y & 1) re = cheng(re, x); x = cheng(x, x); y >>= 1;} return re;} void Init() { G = 3; Gv = ksm(G, mo - 2); inv[0] = inv[1] = 1; for (int i = 2; i < N; i++) inv[i] = cheng(inv[mo % i], mo - mo / i); } void get_an(int limit, int l_size) { for (int i = 0; i < limit; i++) an[i] = (an[i >> 1] >> 1) | ((i & 1) << (l_size - 1)); } void NTT(int *f, int limit, int op) { for (int i = 0; i < limit; i++) if (an[i] < i) swap(f[an[i]], f[i]); for (int mid = 1; mid < limit; mid <<= 1) { int Wn = ksm(op == 1 ? G : Gv, (mo - 1) / (mid << 1)); for (int R = (mid << 1), j = 0; j < limit; j += R) { int w = 1; for (int k = 0; k < mid; k++, w = cheng(w, Wn)) { int x = f[j | k], y = cheng(w, f[j | mid | k]); f[j | k] = jia(x, y); f[j | mid | k] = jian(x, y); } } } if (op == -1) { int limv = ksm(limit, mo - 2); for (int i = 0; i < limit; i++) f[i] = cheng(f[i], limv); } } void px(int *f, int *g, int limit) { for (int i = 0; i < limit; i++) f[i] = cheng(f[i], g[i]); } int A[N], B[N]; void slove(int *f, int *g, int l, int r) { if (l == r) return ; int mid = (l + r) >> 1, sz = r - l + 1; slove(f, g, l, mid); cpy(A, f + l, mid - l + 1); cpy(B, g, sz); int limit = 1, l_size = 0; while (limit <= sz) limit <<= 1, l_size++; get_an(limit, l_size); clr(A + mid - l + 1, limit - (mid - l + 1)); clr(B + sz, limit - sz); NTT(A, limit, 1); NTT(B, limit, 1); px(A, B, limit); NTT(A, limit, -1); for (int i = mid + 1; i <= r; i++) f[i] = jia(f[i], A[i - l]); slove(f, g, mid + 1, r); } int main() { Init(); scanf("%d", &n); for (int i = 1; i < n; i++) scanf("%d", &g[i]); f[0] = 1; slove(f, g, 0, n - 1); for (int i = 0; i < n; i++) printf("%d ", f[i]); return 0; }

多项式求逆

#include<cstdio> #include<cstring> #include<algorithm> #define mo 998244353 #define clr(f, n) memset(f, 0, (n) * sizeof(int)) #define cpy(f, g, n) memcpy(f, g, (n) * sizeof(int)) using namespace std; const int N = 100000 * 8 + 1; int n, f[N], g[N], an[N], inv[N], G, Gv; int jia(int x, int y) {return x + y >= mo ? x + y - mo : x + y;} int jian(int x, int y) {return x - y < 0 ? x - y + mo : x - y;} int cheng(int x, int y) {return 1ll * x * y % mo;} int ksm(int x, int y) {int re = 1; while (y) {if (y & 1) re = cheng(re, x); x = cheng(x, x); y >>= 1;} return re;} void Init() { G = 3; Gv = ksm(G, mo - 2); inv[0] = inv[1] = 1; for (int i = 2; i < N; i++) inv[i] = cheng(inv[mo % i], mo - mo / i); } void get_an(int limit, int l_size) { for (int i = 0; i < limit; i++) an[i] = (an[i >> 1] >> 1) | ((i & 1) << (l_size - 1)); } void NTT(int *f, int limit, int op) { for (int i = 0; i < limit; i++) if (an[i] < i) swap(f[an[i]], f[i]); for (int mid = 1; mid < limit; mid <<= 1) { int Wn = ksm(op == 1 ? G : Gv, (mo - 1) / (mid << 1)); for (int R = (mid << 1), j = 0; j < limit; j += R) { int w = 1; for (int k = 0; k < mid; k++, w = cheng(w, Wn)) { int x = f[j | k], y = cheng(w, f[j | mid | k]); f[j | k] = jia(x, y); f[j | mid | k] = jian(x, y); } } } if (op == -1) { int limv = ksm(limit, mo - 2); for (int i = 0; i < limit; i++) f[i] = cheng(f[i], limv); } } void px(int *f, int *g, int limit) { for (int i = 0; i < limit; i++) f[i] = cheng(f[i], g[i]); } void times(int *f, int *g, int n, int m) { static int tmp[N]; int limit = 1, l_size = 0; while (limit < n + n) limit <<= 1, l_size++; cpy(tmp, g, n); clr(tmp + n, limit - n); get_an(limit, l_size); NTT(f, limit, 1); NTT(tmp, limit, 1); px(f, tmp, limit); NTT(f, limit, -1); clr(f + m, limit - m); clr(tmp, limit); } void invp(int *f, int n) { static int w[N], r[N], tmp[N]; w[0] = ksm(f[0], mo - 2); int limit = 1, l_size = 0; for (int len = 2; (len >> 1) <= n; len <<= 1) { limit = len; l_size++; get_an(limit, l_size); cpy(r, w, len >> 1); cpy(tmp, f, limit); NTT(tmp, limit, 1); NTT(r, limit, 1); px(r, tmp, limit); NTT(r, limit, -1); clr(r, limit >> 1); cpy(tmp, w, len); NTT(tmp, limit, 1); NTT(r, limit, 1); px(r, tmp, limit); NTT(r, limit, -1); for (int i = (len >> 1); i < len; i++) w[i] = jian(cheng(w[i], 2), r[i]); } cpy(f, w, n); clr(w, limit); clr(r, limit); clr(tmp, limit); } void dao(int *f, int n) { for (int i = 1; i < n; i++) f[i - 1] = cheng(f[i], i); f[n - 1] = 0; } void jifen(int *f, int n) { for (int i = n; i >= 1; i--) f[i] = cheng(f[i - 1], inv[i]); f[0] = 0; } void mof(int *f, int n, int *g, int m) { static int f_[N], g_[N]; int L = n - m + 1; reverse(f, f + n); cpy(f_, f, L); reverse(f, f + n); reverse(g, g + m); cpy(g_ , g, L); reverse(g, g + m); invp(g_, L); times(g_, f_, L, L); reverse(g_, g_ + L); times(g, g_, n, n); for (int i = 0; i < m - 1; i++) g[i] = jian(f[i], g[i]); clr(g + m - 1, L); cpy(f, g_, L); clr(f + L, n - L); } void lnp(int *f, int n) { static int g[N]; cpy(g, f, n); dao(g, n); invp(f, n); times(f, g, n, n); jifen(f, n - 1); clr(g, n); } void exp(int *f, int n) { static int w[N], ww[N]; ww[0] = 1; int len; for (len = 2; (len >> 1) <= n; len <<= 1) { cpy(w, ww, len >> 1); lnp(w, len); for (int i = 0; i < len; i++) w[i] = jian(f[i], w[i]); w[0] = jia(w[0], 1); times(ww, w, len, len); } len >>= 1; cpy(f, ww, n); clr(ww, len); clr(w, len); } void ksmp(int *f, int n, int k) { lnp(f, n); for (int i = 0; i < n; i++) f[i] = cheng(f[i], k); exp(f, n); } int main() { Init(); scanf("%d", &n); for (int i = 1; i < n; i++) scanf("%d", &f[i]); int f0 = 1; for (int i = 0; i <= n; i++) f[i] = jian((i == 0), f[i]); invp(f, n); for (int i = 1; i <= n; i++) f[i] = cheng(f0, f[i]); for (int i = 0; i < n; i++) printf("%d ", f[i]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P4721.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-03-26 【ybt金牌导航8-7-1】数对统计 / 关于莫比乌斯函数的少量内容
点击右上角即可分享
微信分享提示