zoukankan      html  css  js  c++  java
  • $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("
    ");
    	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("
    ");
    	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("
    ");
    	rin(i,0,n-1) printf("%d ",B[i]);
    	printf("
    ");
    	rin(i,0,n-1) printf("%d ",C[i]);
    	printf("
    ");
    	return 0;
    }
    

    (FFT/NTT/FWT)的关键

    找到在同一条件下的不变量,让其成为两个多项式卷积时下标的目标。

    (FFT/NTT)习题

    [BZOJ2179]FFT快速傅立叶

    (FFT)优化高精乘,因为没有取模的问题所以可能在这里(NTT)的常数要优于(FFT)

    [BZOJ2194]快速傅立叶之二

    (b)数组(reverse()),发现原来的式子变成了:

    [C[k]=sum(a[i] imes b[n+k-1-i]) ]

    这是一个卷积的形式,(FFT)即可。

    [BZOJ3527]力

    博主之前的博客写过。链接

    [BZOJ3160]万径人踪灭

    我 卷 我 自 己

    分别计算(a)(b)对答案的贡献,然后(Manacher)减掉不合法的方案。

    [BZOJ4503]两个串

    [BZOJ2194]快速傅立叶之二出发,判断两个字符串是否匹配可以通过作差后平方转化为卷积的形式。由于通配符的存在外面还需要再乘一个(T[i])

    [BZOJ4827][Hnoi2017]礼物

    (c)的最优值一定为二次函数顶点,剩下的就是一个卷积了。

    [HDU4609]3-idiots

    先把生成函数搞出来,用(FFT)乘起来,把不合法的减去即可。

    [BZOJ3625][Codeforces Round #250]小朋友和二叉树

    跟据题意一波分析可得:

    [F(x) equiv F(x)^2 imes G(x)+1 (mod x^{m+1}) ]

    (G(x))(c)的生成函数。

    一元二次方程的求根公式搞上去,多项式开方加多项式求逆计算答案。

    [BZOJ3509][CodeChef]COUNTARI

    式子可以化为:

    [2 imes A[j]=A[i]+A[k] ]

    分块,块外(FFT),块内暴力即可。

    [BZOJ3771]Triple

    Something about 一般型生成函数里面讲过。

    [UVA12633]Super Rooks on Chessboard

    发现一条对角线可以用行标和列标的差表示,这令我们又想到了[BZOJ2194]快速傅立叶之二。可以将列标翻转,先只考虑棋子对整行和整列的影响,构造两个多项式,分别表示整行和整列的覆盖情况,如果一行或一列上没有棋子,那么对应多项式的相应次项的系数就是(1),否则是(0)。然后就可以(FFT)求出每条对角线上有几个没被覆盖的格子。最后考虑每条对角线是否对答案产生贡献即可。

    [HDU5307]He is Flying

    这道题有点神,博主一开始没想到什么靠谱的思路。

    于是我们可以考虑上网搜题解。

    题解告诉我们可以构造这样一个答案的生成函数:

    [F(x)=(sum_{i=1}^nix^{sum_i}) imes (sum_{i=1}^nx^{-sum_{i-1}})-(sum_{i=1}^nx^{sum_i}) imes (sum_{i=1}^n(i-1)x^{sum_{i-1}}) ]

    特别的,需要单独计算(s=0)时的答案,(O(n))扫一遍即可。

    (FWT)习题

    [HDU5909]Tree Cutting

    (F[i](x))表示以(i)为根的子树的生成函数,树形(DP),合并时使用(FWT)即可。

    注意子图必须连通。

    [BZOJ4589]Hard Nim

    根据博弈论相关知识,(NanoApe)能获胜当且仅当所有堆石子异或和是负数。

    搞出一堆石子的生成函数,然后(FWT Rightarrow QPOW Rightarrow IFWT)即可。

    [CF662C]Binary Table

    发现在变换的行一定时,所有列的初始状态和结束状态的(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))最小的系数即为答案。

    [BZOJ4036][HAOI2015]按位或

    根据(min-max)容斥,(E(max{S})=sum_{T subseteq S}(-1)^{|T|+1}E(min{T}))

    (E(min{T}))可以通过补集求,需要用到(FWT)

  • 相关阅读:
    [杭电_HDU] 2013
    动态调整线程数的python爬虫代码分享
    wampserver 配置的几个坑(雾
    wampserver apache 403无权限访问 You don't have permission to access /index.html on this server
    [爬坑日记] 安卓模拟器1903蓝屏 没开hyper-v
    [单片机] ESP8266 开机自动透传
    [操作系统] 死锁预防和死锁避免
    [linux] 手机Deploy linux 桌面中文乱码
    XHTML基础
    JDBC_c3p0连接池
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10023224.html
Copyright © 2011-2022 走看看