分治FFT

目的

解决这样一类式子:

\[f[n] = \sum_{i = 0}^{n - 1}f[i]g[n - i]
\]

算法

看上去跟普通卷积式子挺像的,但是由于计算\(f\)的每一项时都在利用它前面的项来产生贡献,所以不能一次FFT搞完。用FFT爆算复杂度\(O(n^2logn)\),比直接枚举复杂度还高……

考虑优化这个算法,如果我们要计算区间\([l, r]\)内的\(f\)值,如果可以快速算出区间\([l, mid]\)内的\(f\)值对区间\([mid + 1, r]\)内的\(f\)值产生了怎样的影响,就可以采取CDQ分治,不断递归下去算。

考虑\(x \in [mid + 1, r]\),\([l, mid]\)给它的贡献是:

\[h[x] = \sum_{i = l}^{mid}f[i]g[x - i]
\]

为了方便,我们将范围扩充到\([1, x - 1]\)(假设此时\(f[mid + 1] ... f[r] = 0\)),因此有:

\[h[x] = \sum_{i = l}^{x - 1}f[i]g[x - i]
\]

为了便于FFT计算,将枚举改成从0开始。(把表达式中的\(i\)改成\(i + l\),因为原来的\(i\)等于现在的\(i + l\))

\[h[x] = \sum_{i = 0}^{x - l - 1}f[i + l]g[x - l - i]
\]

为了表示成卷积形式,我们令:

\[a[i] = f[i + l], b[i - 1] = g[i]
\]

再在原式中用\(a[i], b[i]\)代替\(f[i], g[i]\).

\[h[x] = \sum_{i = 0}^{x - l - 1}a[i] b[x - l - 1 - i]
\]

观察到后面刚好就是多项式乘法中某一项的系数,即

\[h[x] = (a * b)(x - l - 1)
\]

在cdq分治的过程中用FFT/NTT计算即可。

代码

洛谷上的模板,因为要取模,所以用的NTT

#include<bits/stdc++.h>
using namespace std;
#define R register int
#define p 998244353
#define AC 400100
#define LL long long
#define ld double const int G = 3, Gi = 332748118;
int n, lim, len;
int f[AC], g[AC], a[AC], b[AC], rev[AC]; inline int read()
{
int x = 0;char c = getchar();
while(c > '9' || c < '0') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
} inline void up(int &a, int b) {a += b; if(a < 0) a += p; if(a >= p) a -= p;} inline int qpow(int x, int have)
{
int rnt = 1;
while(have)
{
if(have & 1) rnt = 1LL * rnt * x % p;
x = 1LL * x * x % p, have >>= 1;
}
return rnt;
} void init(int length)//这里的length已经是2个数组加起来的长度了
{
lim = 1, len = 0;
while(lim <= length) lim <<= 1, ++ len;
for(R i = 0; i < lim; i ++)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (len - 1)), a[i] = b[i] = 0;
} void NTT(int *A, int opt)
{
for(R i = 0; i < lim; i ++)
if(i < rev[i]) swap(A[i], A[rev[i]]);
for(R i = 1; i < lim; i <<= 1)
{
LL W = qpow((opt > 0) ? G : Gi, (p - 1) / (i << 1));
for(R r = i << 1, j = 0; j < lim; j += r)
for(R k = 0, w = 1; k < i; k ++, w = 1LL * w * W % p)
{
int x = A[j + k], y = 1LL * w * A[j + k + i] % p;
A[j + k] = (x + y) % p, A[j + k + i] = (x - y) % p;
}
}
if(opt == -1)
{
int inv = qpow(lim, p - 2);
for(R i = 0; i < lim; i ++) A[i] = 1LL * A[i] * inv % p;
}
} void pre()
{
n = read(), f[0] = 1;
for(R i = 1; i < n; i ++) g[i] = read();
} void cal(int *A, int *B)
{
NTT(A, 1), NTT(B, 1);
for(R i = 0; i <= lim; i ++) A[i] = 1LL * A[i] * B[i] % p;
NTT(A, -1);
} void cdq(int l, int r)
{
if(l == r) return ;
int mid = (l + r) >> 1, length = r - l + 1;
cdq(l, mid);
init(length);
for(R i = l; i <= mid; i ++) a[i - l] = f[i];
for(R i = 1; i < length; i ++) b[i - 1] = g[i];//这里要移动是为了凑x - l - 1
cal(a, b);
for(R i = mid + 1; i <= r; i ++) up(f[i], a[i - l - 1]);
cdq(mid + 1, r);
} int main()
{
freopen("in.in", "r", stdin);
pre();
cdq(0, n - 1);
for(R i = 0; i < n; i ++) printf("%d ", ((f[i] % p) + p) % p);
printf("\n");
fclose(stdin);
return 0;
}

分治FFT的更多相关文章

  1. BNUOJ 51279[组队活动 Large](cdq分治+FFT)

    传送门 大意:ACM校队一共有n名队员,从1到n标号,现在n名队员要组成若干支队伍,每支队伍至多有m名队员,求一共有多少种不同的组队方案.两个组队方案被视为不同的,当且仅当存在至少一名队员在两种方案中 ...

  2. hdu 5730 Shell Necklace [分治fft | 多项式求逆]

    hdu 5730 Shell Necklace 题意:求递推式\(f_n = \sum_{i=1}^n a_i f_{n-i}\),模313 多么优秀的模板题 可以用分治fft,也可以多项式求逆 分治 ...

  3. BZOJ 4555: [Tjoi2016&Heoi2016]求和 [分治FFT 组合计数 | 多项式求逆]

    4555: [Tjoi2016&Heoi2016]求和 题意:求\[ \sum_{i=0}^n \sum_{j=0}^i S(i,j)\cdot 2^j\cdot j! \\ S是第二类斯特林 ...

  4. 分治FFT的三种含义

    分治FFT是几个算法的统称.它们之间并无关联. 分治多项式乘法 问题如求\(\prod_{i=1}^na_ix+b\). 若挨个乘复杂度为\(O(n^2\log n)\),可分治做这件事,复杂度为\( ...

  5. 【XSY2666】排列问题 DP 容斥原理 分治FFT

    题目大意 有\(n\)种颜色的球,第\(i\)种有\(a_i\)个.设\(m=\sum a_i\).你要把这\(m\)个小球排成一排.有\(q\)个询问,每次给你一个\(x\),问你有多少种方案使得相 ...

  6. 【XSY2887】【GDOI2018】小学生图论题 分治FFT 多项式exp

    题目描述 在一个 \(n\) 个点的有向图中,编号从 \(1\) 到 \(n\),任意两个点之间都有且仅有一条有向边.现在已知一些单向的简单路径(路径上任意两点各不相同),例如 \(2\to 4\to ...

  7. prime distance on a tree(点分治+fft)

    最裸的点分治+fft,调了好久,太菜了.... #include<iostream> #include<cstring> #include<cstdio> #inc ...

  8. 【XSY2744】信仰圣光 分治FFT 多项式exp 容斥原理

    题目描述 有一个\(n\)个元素的置换,你要选择\(k\)个元素,问有多少种方案满足:对于每个轮换,你都选择了其中的一个元素. 对\(998244353\)取模. \(k\leq n\leq 1525 ...

  9. 【BZOJ5119】【CTT2017】生成树计数 DP 分治FFT 斯特林数

    CTT=清华集训 题目大意 有\(n\)个点,点权为\(a_i\),你要连接一条边,使该图变成一颗树. 对于一种连边方案\(T\),设第\(i\)个点的度数为\(d_i\),那么这棵树的价值为: \[ ...

  10. 【XSY2166】Hope 分治 FFT

    题目描述 对于一个\(1\)到\(n\)的排列\(a_1,a_2,a_3,\ldots,a_n\),我们定义这个排列的\(P\)值和\(Q\)值: 对于每个\(a_i\),如果存在一个最小的\(j\) ...

随机推荐

  1. NumPy v1.15手册汉化

    NumPy参考 数组创建 零 和 一 empty(shape[, dtype, order]):返回给定形状和类型的新数组,而不初始化条目 empty_like(prototype[, dtype,  ...

  2. Python爬虫初探 - selenium+beautifulsoup4+chromedriver爬取需要登录的网页信息

    目标 之前的自动答复机器人需要从一个内部网页上获取的消息用于回复一些问题,但是没有对应的查询api,于是想到了用脚本模拟浏览器访问网站爬取内容返回给用户.详细介绍了第一次探索python爬虫的坑. 准 ...

  3. 百度地图之自动提示--autoComplete

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 423. Valid Parentheses【LintCode java】

    Description Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine ...

  5. 利用xlsxwriter生成数据报表

    #!/usr/bin/env python# -*- coding:utf-8 -*-import os,xlsxwriter,datetimeimport ConfigParserfrom send ...

  6. C语言—单链表

    单链表操作:读取,插入和删除 #include "stdafx.h" #include <string.h> #include <stdio.h> #inc ...

  7. 使用SKlearn(Sci-Kit Learn)进行SVR模型学习

    今天了解到sklearn这个库,简直太酷炫,一行代码完成机器学习. 贴一个自动生成数据,SVR进行数据拟合的代码,附带网格搜索(GridSearch, 帮助你选择合适的参数)以及模型保存.读取以及结果 ...

  8. tomcat 最大并发连接数设置

    转自: http://blog.csdn.net/qysh123/article/details/11678903 这是个很简单的问题,但是搜了一圈,发现大家都写错了.所以这里总结一下: 几乎所有的中 ...

  9. redis 常用命令 结合php

    这篇文章主要介绍了30个php操作redis常用方法代码例子,本文其实不止30个方法,可以操作string类型.list类型和set类型的数据,需要的朋友可以参考下     redis的操作很多的,以 ...

  10. The Bits (思维+找规律)

    Description Rudolf is on his way to the castle. Before getting into the castle, the security staff a ...