$FFT/NTT/FWT$题单&简要题解
打算写一个多项式总结。
虽然自己菜得太真实了。
好像四级标题太小了,下次写博客的时候再考虑一下。
模板
\(FFT\)模板
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#define rin(i,a,b) for(int i=(a);i<=(b);i++)
#define rec(i,a,b) for(int i=(a);i>=(b);i--)
#define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
using std::cin;
using std::cout;
using std::endl;
typedef long long LL;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x;
}
const int MAXN=1000005;
const int MAXLEN=2100005;
const double pi=std::acos(-1);
int n,m;
int len,rev[MAXLEN];
struct Complex{
double real,imag;
inline friend Complex operator + (Complex x,Complex y){
return (Complex){x.real+y.real,x.imag+y.imag};
}
inline friend Complex operator - (Complex x,Complex y){
return (Complex){x.real-y.real,x.imag-y.imag};
}
inline friend Complex operator * (Complex x,Complex y){
return (Complex){x.real*y.real-x.imag*y.imag,x.real*y.imag+x.imag*y.real};
}
};
Complex A[MAXLEN],B[MAXLEN];
inline void fft(Complex *c,int dft){
rin(i,0,n-1) if(i<rev[i])
std::swap(c[i],c[rev[i]]);
for(int mid=1;mid<n;mid<<=1){
int r=(mid<<1);
Complex u=(Complex){std::cos(pi/mid),dft*std::sin(pi/mid)};
for(int l=0;l<n;l+=r){
Complex v=(Complex){1,0};
for(int i=0;i<mid;i++,v=v*u){
Complex x=c[l+i],y=c[l+mid+i]*v;
c[l+i]=x+y;
c[l+mid+i]=x-y;
}
}
}
if(dft<0) rin(i,0,n-1)
c[i].real/=n;
}
int main(){
n=read(),m=read();
rin(i,0,n) A[i].real=read();
rin(i,0,m) B[i].real=read();
m+=n;
for(n=1;n<=m;n<<=1) len++;
rin(i,1,n-1) rev[i]=((rev[i>>1]>>1)|((i&1)<<(len-1)));
fft(A,1);
fft(B,1);
rin(i,0,n-1) A[i]=A[i]*B[i];
fft(A,-1);
rin(i,0,m) printf("%d ",(int)(A[i].real+0.5));
printf("\n");
return 0;
}
\(NTT\)模板
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#define rin(i,a,b) for(int i=(a);i<=(b);i++)
#define rec(i,a,b) for(int i=(a);i>=(b);i--)
#define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
using std::cin;
using std::cout;
using std::endl;
typedef long long LL;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x;
}
const int MAXN=1000005;
const int MAXLEN=2100005;
const LL MOD=998244353,G=3,INVG=332748118;
int n,m,invn;
int len,rev[MAXLEN];
LL A[MAXLEN],B[MAXLEN];
inline LL qpow(LL x,LL y){
LL ret=1,tt=x%MOD;
while(y){
if(y&1) ret=ret*tt%MOD;
tt=tt*tt%MOD;
y>>=1;
}
return ret;
}
inline void ntt(LL *c,int dft){
rin(i,0,n-1) if(i<rev[i])
std::swap(c[i],c[rev[i]]);
for(int mid=1;mid<n;mid<<=1){
int r=(mid<<1);
LL u=qpow(dft>0?G:INVG,(MOD-1)/r);
for(int l=0;l<n;l+=r){
LL v=1;
for(int i=0;i<mid;i++,v=v*u%MOD){
LL x=c[l+i],y=c[l+mid+i]*v%MOD;
c[l+i]=x+y;
if(c[l+i]>=MOD) c[l+i]-=MOD;
c[l+mid+i]=x-y;
if(c[l+mid+i]<0) c[l+mid+i]+=MOD;
}
}
}
if(dft<0) rin(i,0,n-1)
c[i]=c[i]*invn%MOD;
}
int main(){
n=read(),m=read();
rin(i,0,n) A[i]=read();
rin(i,0,m) B[i]=read();
m+=n;
for(n=1;n<=m;n<<=1) len++;
rin(i,1,n-1) rev[i]=((rev[i>>1]>>1)|((i&1)<<(len-1)));
invn=qpow(n,MOD-2);
ntt(A,1);
ntt(B,1);
rin(i,0,n-1) A[i]=A[i]*B[i]%MOD;
ntt(A,-1);
rin(i,0,m) printf("%lld ",A[i]);
printf("\n");
return 0;
}
\(FWT\)模板
前方毒瘤码风警告!!!
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#define rin(i,a,b) for(int i=(a);i<=(b);i++)
#define rec(i,a,b) for(int i=(a);i>=(b);i--)
#define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
using std::cin;
using std::cout;
using std::endl;
typedef long long LL;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x;
}
const int MAXN=135005;
const int MOD=998244353;
const int INV2=499122177;
int n;
int len;
int a[MAXN],b[MAXN];
int A[MAXN],B[MAXN],C[MAXN],D[MAXN];
inline void fwt(int *c,const char *s,int opt){
for(int mid=1;mid<n;mid<<=1){
int r=(mid<<1);
for(int l=0;l<n;l+=r){
for(int i=0;i<mid;i++){
if(s=="or"){
if(opt>0){
c[l+mid+i]+=c[l+i];
if(c[l+mid+i]>=MOD) c[l+mid+i]-=MOD;
}
else{
c[l+mid+i]-=c[l+i];
if(c[l+mid+i]<0) c[l+mid+i]+=MOD;
}
}
else if(s=="and"){
if(opt>0){
c[l+i]+=c[l+mid+i];
if(c[l+i]>=MOD) c[l+i]-=MOD;
}
else{
c[l+i]-=c[l+mid+i];
if(c[l+i]<0) c[l+i]+=MOD;
}
}
else{
LL x=c[l+i],y=c[l+mid+i];
c[l+i]=x+y;
c[l+mid+i]=x-y;
if(opt<0){
c[l+i]=1ll*c[l+i]*INV2%MOD;
c[l+mid+i]=1ll*c[l+mid+i]*INV2%MOD;
}
if(c[l+i]>=MOD) c[l+i]-=MOD;
if(c[l+mid+i]<0) c[l+mid+i]+=MOD;
}
}
}
}
}
int main(){
len=read();
n=(1<<len);
rin(i,0,n-1) A[i]=B[i]=C[i]=a[i]=read();
rin(i,0,n-1) D[i]=b[i]=read();
fwt(A,"or",1);
fwt(D,"or",1);
rin(i,0,n-1) A[i]=1ll*A[i]*D[i]%MOD;
fwt(A,"or",-1);
rin(i,0,n-1) D[i]=b[i];
fwt(B,"and",1);
fwt(D,"and",1);
rin(i,0,n-1) B[i]=1ll*B[i]*D[i]%MOD;
fwt(B,"and",-1);
rin(i,0,n-1) D[i]=b[i];
fwt(C,"xor",1);
fwt(D,"xor",1);
rin(i,0,n-1) C[i]=1ll*C[i]*D[i]%MOD;
fwt(C,"xor",-1);
rin(i,0,n-1) printf("%d ",A[i]);
printf("\n");
rin(i,0,n-1) printf("%d ",B[i]);
printf("\n");
rin(i,0,n-1) printf("%d ",C[i]);
printf("\n");
return 0;
}
\(FFT/NTT/FWT\)的关键
找到在同一条件下的不变量,让其成为两个多项式卷积时下标的目标。
\(FFT/NTT\)习题
\(FFT\)优化高精乘,因为没有取模的问题所以可能在这里\(NTT\)的常数要优于\(FFT\)。
将\(b\)数组\(reverse()\),发现原来的式子变成了:
\]
这是一个卷积的形式,\(FFT\)即可。
博主之前的博客写过。链接
我 卷 我 自 己
分别计算\(a\)和\(b\)对答案的贡献,然后\(Manacher\)减掉不合法的方案。
从[BZOJ2194]快速傅立叶之二出发,判断两个字符串是否匹配可以通过作差后平方转化为卷积的形式。由于通配符的存在外面还需要再乘一个\(T[i]\)。
\(c\)的最优值一定为二次函数顶点,剩下的就是一个卷积了。
先把生成函数搞出来,用\(FFT\)乘起来,把不合法的减去即可。
[BZOJ3625][Codeforces Round #250]小朋友和二叉树
跟据题意一波分析可得:
\]
\(G(x)\)是\(c\)的生成函数。
一元二次方程的求根公式搞上去,多项式开方加多项式求逆计算答案。
式子可以化为:
\]
分块,块外\(FFT\),块内暴力即可。
[UVA12633]Super Rooks on Chessboard
发现一条对角线可以用行标和列标的差表示,这令我们又想到了[BZOJ2194]快速傅立叶之二。可以将列标翻转,先只考虑棋子对整行和整列的影响,构造两个多项式,分别表示整行和整列的覆盖情况,如果一行或一列上没有棋子,那么对应多项式的相应次项的系数就是\(1\),否则是\(0\)。然后就可以\(FFT\)求出每条对角线上有几个没被覆盖的格子。最后考虑每条对角线是否对答案产生贡献即可。
这道题有点神,博主一开始没想到什么靠谱的思路。
于是我们可以考虑上网搜题解。
题解告诉我们可以构造这样一个答案的生成函数:
\]
特别的,需要单独计算\(s=0\)时的答案,\(O(n)\)扫一遍即可。
\(FWT\)习题
\(F[i](x)\)表示以\(i\)为根的子树的生成函数,树形\(DP\),合并时使用\(FWT\)即可。
注意子图必须连通。
根据博弈论相关知识,\(NanoApe\)能获胜当且仅当所有堆石子异或和是负数。
搞出一堆石子的生成函数,然后\(FWT \Rightarrow QPOW \Rightarrow IFWT\)即可。
发现在变换的行一定时,所有列的初始状态和结束状态的\(xor\)一定(暂时不考虑列的变换)。\(G(x)\)统计每个初始状态的数量,\(H(x)\)表示每个结束状态的贡献,即\(H(x)\)的\(i\)次项系数\(h_i=min(\_\_builtin\_popcount(i),n-\_\_builtin\_popcount(i))\)。
\(F(x)=G(x) \oplus H(x)\),\(F(x)\)最小的系数即为答案。
根据\(min-max\)容斥,\(E(max\{S\})=\sum_{T \subseteq S}(-1)^{|T|+1}E(min\{T\})\)。
\(E(min\{T\})\)可以通过补集求,需要用到\(FWT\)。
随机推荐
- [19/10/16-星期四] Python中的文件操作
一.打开文件 # open(file, mode='r', buffering=-1, encoding_=None, errors=None, newline=None, closefd=True, ...
- 正反向代理、负载均衡、Nginx配置实现
一.正反向代理 1.前提 我们曾经使用翻墙软件,访问google:使用了代理软件时,需要在浏览器选项中配置代理的地址,我们仅仅有代理这个概念,并不清楚代理还有正向和反向之分. 2.正向代理(代替客户端 ...
- Windows node.js安装运行npm显示类似"ENOENT, stat 'C:\Users\XXXX\AppData\Roaming\npm'错误
这个错误是在玩一个小的博客的时候,使用到node.js,正好使用的是windows系统就安装了一个windows32的node.js版本 结果一运行npm就出现如上的错误,后来发现,只要在上面提到的目 ...
- 基于RSA的前后端登陆密码加密JAVA实现(转)
RSA加密算法简介 SA加密算法是一种非对称加密算法.在公开密钥加密和电子商业中RSA被广泛使用.对极大整数做因数分解的难度决定了RSA算法的可靠性.换言之,对一极大整数做因数分解愈困难,RSA算法愈 ...
- MVCC/分布式事务简介
之前我们学习了RocksDB,但这还只是一个最基础的存储引擎.如果想把它在生产环境中用起来,还需要解决很多问题: 如何从单机扩展到分布式? 如何实现事务,并对事务进行并发控制? 用户接口能不能高级一点 ...
- [七月挑选]IntelliJ IDEA常用设置
title: IntelliJ IDEA常用设置 设置idea的类注释快捷键 File -> Settings -> Live Templates 1.右边的 + -> Templa ...
- goaccess实现nginx实现日志可视化
一:goaccess模块 goaccess模块是将nginx的日志定向到goaccess模块里实现,原理很简单,只要将文件定向到那里就可以,goaccess是已经集成好显示数据的一个页面,非常的美观和 ...
- ElasticSearch - 解决ES的深分页问题 (游标 scroll)
https://www.jianshu.com/p/f4d322415d29 1.简介 ES为了避免深分页,不允许使用分页(from&size)查询10000条以后的数据,因此如果要查询第10 ...
- 使用vim打造python-ide
一.前言 一直希望在linux下进行python开发,但是linux不想启动图形化界面,所以干脆直接用上了万能的VIM,用VIM打造了属于自己的python-IDE 二.插件 标签导航(tagbar和 ...
- MFC学习笔记2---简单计算器
前言 学习了鸡啄米网页的前三部分后,我们就可以做一个小软件出来了,我选择先做一个计算器. 这是Win7系统自带的计算器: 为了提升成就感,我将计算器的大部分内容去除,于是就变成这样: 这样就只剩下了1 ...