题目:

洛谷 4721

分析:

我觉得这个 “分治 FFT ” 不能算一种特殊的 FFT ,只是 CDQ 分治里套了个用 FFT (或 NTT)计算的过程,二者是并列关系而不是偏正关系,跟 CDQ 分治套树状数组之类性质差不多吧(所以我也不知道为什么洛谷要把这个作为一个模板)。

言归正传,先看一眼原来的式子:

\[f[i]=\begin{cases}1\ (i=0)\\\sum_{j=1}^{i}f[i-j]g[j]\ \mathrm{otherwise}\end{cases}
\]

\(f[i]=\sum f[i-j]g[j]\) 很像一个多项式卷积,只是后面的值要用到前面的值,不能直接卷积。考虑 CDQ 分治计算区间 \([l,r]\) 的一般过程:先递归左区间 \([l,mid]\) ,再计算左区间的值对右区间的值的贡献,最后递归右区间 \((mid,r]\) 。

如何计算 “左区间的值对右区间的值的贡献” 呢?考虑 \(f[i](l\leq i\leq mid)\) 这一项对 \((mid,r]\) 的贡献:

\[f[i+j]=\sum f[i]g[j] (i\in [l, mid], j\in [0, r-i])
\]

(注意 \(i+j\leq mid\) 的情况已经在递归左区间时计算过,直接忽略掉即可)

设多项式 \(A[i-l]=\begin{cases}f[i](l\leq i \leq mid)\\0(mid<i\leq r)\end{cases}\) (后半部分置 \(0\) 凑够长度),则有:

\[f[l+i+j]=\sum A[i]g[j] (i\in [0, r-l], j\in [0, r-l-i])
\]

写得更清晰一点:

\[f[l+i]=\sum A[j]g[i-j] (i\in [0, r-l], j\in [0, i])
\]

这个直接拿卷积算就好了。

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std; namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return false;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
typedef long long ll;
const int N = 1e5 + 10, LEN = N << 2, p = 998244353, g = 3;
inline int power(int a, int b)
{
int ans = 1;
while (b)
{
if(b & 1)
ans = (ll)ans * a % p;
a = (ll)a * a % p;
b >>= 1;
}
return ans;
}
inline int inv(const int a)
{
return power(a, p - 2);
}
namespace Polynomial
{
int rev[LEN], omega[LEN], winv[LEN];
void init(const int n, const int lg2)
{
int w = power(g, (p - 1) / n), wi = inv(w);
omega[0] = winv[0] = 1;
for (int i = 1; i < n; i++)
{
omega[i] = (ll)omega[i - 1] * w % p;
winv[i] = (ll)winv[i - 1] * wi % p;
}
for (int i = 0; i < n; i++)
rev[i] = ((rev[i >> 1] >> 1) | ((i & 1) << (lg2 - 1)));
}
void ntt(int *a, const int *w, const int n)
{
for (int i = 0; i < n; i++)
if (i < rev[i])
swap(a[i], a[rev[i]]);
for (int l = 1; l < n; l <<= 1)
for (int i = 0; i < n; i += (l << 1))
for (int k = 0; k < l; k++)
{
int x = a[i + k], y = (ll)w[n / (l << 1) * k] * a[i + l + k] % p;
a[i + k] = (x + y) % p;
a[i + l + k] = (x - y + p) % p;
}
}
void mul(const int *a, const int *b, int *c, const int n)
{
static int x[LEN], y[LEN];
int m = 1, lg2 = 0;
while (m < n + n - 1)
m <<= 1, ++lg2;
memcpy(x, a, sizeof(int[n]));
memset(x + n, 0, sizeof(int[m - n]));
memcpy(y, b, sizeof(int[n]));
memset(y + n, 0, sizeof(int[m - n]));
init(m, lg2);
ntt(x, omega, m), ntt(y, omega, m);
for (int i = 0; i < m; i++)
x[i] = (ll)x[i] * y[i] % p;
ntt(x, winv, m);
int invm = inv(m);
for (int i = 0; i < n; i++)
c[i] = (ll)x[i] * invm % p;
}
}
int arr[N], ans[N], n;
void solve(const int l, const int r)
{
static int tmp1[N], tmp2[N];
if (l == r)
return;
int mid = (l + r) >> 1;
solve(l, mid);
for (int i = l; i <= mid; i++)
tmp1[i - l] = ans[i];
for (int i = mid + 1; i <= r; i++)
tmp1[i - l] = 0;
Polynomial::mul(arr, tmp1, tmp2, r - l + 1);
for (int i = mid + 1; i <= r; i++)
ans[i] = (ans[i] + tmp2[i - l]) % p;
solve(mid + 1, r);
}
int work()
{
read(n);
for (int i = 1; i < n; i++)
read(arr[i]);
ans[0] = 1;
solve(0, n - 1);
for (int i = 0; i < n; i++)
write(ans[i]), putchar(' ');
return 0;
}
}
int main()
{
freopen("4721.in", "r", stdin);
return zyt::work();
}

【洛谷4721】【模板】分治FFT(CDQ分治_NTT)的更多相关文章

  1. 洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP

    洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他. 玩具上有一个数列,数列中某些项的值可能会 ...

  2. 洛谷.4721.[模板]分治FFT(NTT)

    题目链接 换一下形式:\[f_i=\sum_{j=0}^{i-1}f_jg_{i-j}\] 然后就是分治FFT模板了\[f_{i,i\in[mid+1,r]}=\sum_{j=l}^{mid}f_jg ...

  3. LOJ 2353 & 洛谷 P4027 [NOI2007]货币兑换(CDQ 分治维护斜率优化)

    题目传送门 纪念一下第一道(?)自己 yy 出来的 NOI 题. 考虑 dp,\(dp[i]\) 表示到第 \(i\) 天最多有多少钱. 那么有 \(dp[i]=\max\{\max\limits_{ ...

  4. 洛谷 P5331 - [SNOI2019]通信(CDQ 分治优化建图+费用流)

    题面传送门 首先熟悉网络流的同学应该能一眼看出此题的建模方法: 将每个点拆成两个点 \(in_i,out_i\),连一条 \(S\to in_i\),容量为 \(1\) 费用为 \(0\) 的边 连一 ...

  5. 解题:洛谷4721 [模板]分治FFT

    题面 这是CDQ入门题,不要被题目名骗了,这核心根本不在不在FFT上啊=.= 因为后面的项的计算依赖于前面的项,不能直接FFT.所以用CDQ的思想,算出前面然后考虑给后面的贡献 #include< ...

  6. hdu 5830 FFT + cdq分治

    Shell Necklace Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)T ...

  7. COGS 2479. [HZOI 2016]偏序 [CDQ分治套CDQ分治 四维偏序]

    传送门 给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 对于100%的 ...

  8. 洛谷P3373 [模板]线段树 2(区间增减.乘 区间求和)

    To 洛谷.3373 [模板]线段树2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格 ...

  9. 洛谷 4721 【模板】分治 FFT——分治FFT / 多项式求逆

    题目:https://www.luogu.org/problemnew/show/P4721 分治FFT:https://www.cnblogs.com/bztMinamoto/p/9749557.h ...

随机推荐

  1. java springMVC 极致验证 非demo版

    最近公司项目需要,做了个极致验证,自己在此做下记录. 先上效果图: 它的官网:http://www.geetest.com/   里面有 身份验证.行为验证,  我这使用的为行为验证. 技术文档:ht ...

  2. uva10537 最短路 倒推

    题意:知道了,最后需要的,那么就倒着最短路,推出去就可以了. 以最短路的方式来解决.

  3. Eclipse4.5在线安装Aptana插件及配置代码提示教程

    一.Aptana插件官网地址         我在网上试过登陆到aptana官网后点击下载,选择下载eclipse插件版,然后页面给出一串地址:http://download.aptana.com/s ...

  4. [NOIP2008] 提高组 洛谷P1125 笨小猴

    题目描述 笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼.但是他找到了一种方法,经试验证明,用这种方法去选择选项的时候选对的几率非常大! 这种方法的具体描述如下:假设maxn是单词中出现次数最 ...

  5. Codeforces679C. Bear and Square Grid

    n<=500,n*n的01矩阵,可以选择一个k*k的矩阵全变1,求最大1联通区域. 敢敢n^3..模拟k*k的矩阵的位置,从左到右扫的时候,每变一个位置只会引起边界的信息变化,就记含边界的k*k ...

  6. mysql控制流程函数(case,if,ifnull,nullif)

    1.case...when... 用法 参考:http://www.cnblogs.com/qlqwjy/p/7476533.html CASE value WHEN [compare-value] ...

  7. Linux下汇编语言学习笔记61 ---

    这是17年暑假学习Linux汇编语言的笔记记录,参考书目为清华大学出版社 Jeff Duntemann著 梁晓辉译<汇编语言基于Linux环境>的书,喜欢看原版书的同学可以看<Ass ...

  8. 不同app之间activity的跳转

    关键在于指定activity的action属性 <activity android:name=".HardActivity"> <intent-filter> ...

  9. WCF - 自定义绑定

    自定义绑定 当系统提供的某个绑定不符合服务的要求时,可使用 CustomBinding 类.所有绑定都是从绑定元素的有序集构造而来的.自定义绑定可以从一组系统提供的绑定元素生成,也可以包含用户定义的自 ...

  10. C++之桟的应用---括号匹配

    刚開始学习数据结构.用桟写了一个经典的应用,括号匹配. 算法思路: 输入字符串时.将 '(' , '['  压入桟.遇到 ')'  ']'  时,再栈顶出桟.进行括号匹配.假设成功匹配.则继续进行.否 ...