zoukankan      html  css  js  c++  java
  • UOJ34 多项式乘法(NTT)

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

    题目链接:UOJ34

    正解:$NTT$

    解题报告:

      $NTT$是用来解决需要取模的一类多项式乘法问题。

      如果要用$NTT$的话,对模数$p$是有要求的:模数要能写成$c*2^k+1$的形式,而且$2^k>n$;

      同时,模数必须要有原根,原根$g$满足的性质是:$g^1,g^2…g^{p-1}$是在模$p$意义下的一个$1$到$p-1$的一个排列。

      回忆一下$FFT$的步骤,中间需要用到单位复数根$w_n$来实现点值表示法,在这里可以直接用$g$的次幂来代替单位复数根,即令$g_n=w_n$,那么$g_n$$=$$g^{frac{p-1}{n}}$。

      其余的做法与$FFT$完全类似。

     

      只是需要注意的是,$FFT$最后插值回去的时候,是取了个反,也就是加了个负号。

      把单位复数根画出来,不难发现,是对称的,取了负号之后其实也就是颠倒了顺序,所以$NTT$的最后需要$reverse$一下。

      注意$0$不用$reverse$,可以认为$0$就是对称轴所以无需考虑。

     

      常用$NTT$模数:

      $998244353$$=$$119*2^{23}+1$,原根为$3$;

      $1004535809$$=$$479*2^{21}+1$,原根为$3$。

      $4179340454199820288$$=$$29*2^{57}+1$,原根为$3$。

      模板保存:

     

    //It is made by ljh2000
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <ctime>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <string>
    #include <bitset>
    using namespace std;
    typedef long long LL;
    const int mod = 998244353;//119*2^23+1
    const int MAXN = 300011;
    const int G = 3;
    int n,m,L,R[MAXN],a[MAXN],b[MAXN];
    
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline LL fast_pow(LL x,LL y){
    	LL r=1;
    	while(y>0) {
    		if(y&1) r*=x,r%=mod;
    		x*=x; x%=mod;
    		y>>=1;
    	}
    	return r;
    }
    
    inline void NTT(int *a,int n,int f){
    	for(int i=0;i<n;i++) if(i<R[i]) swap(a[i],a[R[i]]);
    	for(int i=1;i<n;i<<=1) {
    		LL gn=fast_pow(G,(mod-1)/(i<<1)),x,t;
    		for(int j=0;j<n;j+=(i<<1)) {
    			LL g=1;
    			for(int k=0;k<i;k++,g=1LL*g*gn%mod) {
    				x=a[j+k]; t=1LL*a[j+i+k]*g%mod;
    				a[j+k]=(x+t)%mod;
    				a[j+i+k]=(x-t+mod)%mod;
    			}
    		}
    	}
    	if(f==1) return ;
    	reverse(a+1,a+n); int ni=fast_pow(n,mod-2);
    	for(int i=0;i<=n;i++) a[i]=1LL*a[i]*ni%mod;
    }
    
    inline void work(){
    	n=getint(); m=getint(); 
    	for(int i=0;i<=n;i++) a[i]=getint();
    	for(int i=0;i<=m;i++) b[i]=getint();
    	m+=n; for(n=1;n<=m;n<<=1) L++;
    	for(int i=0;i<n;i++) R[i]=(R[i>>1]>>1) | ((i&1) << (L-1));
    	NTT(a,n,1); NTT(b,n,1);
    	for(int i=0;i<=n;i++) a[i]=1LL*a[i]*b[i]%mod;
    	NTT(a,n,-1);
    	for(int i=0;i<=m;i++) printf("%d ",a[i]);
    }
    
    int main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("NTT.in","r",stdin);
    	freopen("NTT.out","w",stdout);
    #endif
        work();
        return 0;
    }
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    

      

  • 相关阅读:
    Java中的静态方法
    捕获异常代码
    重载与重写的区别
    继承代码
    Android Studio 快捷键一览
    android两种基本联网方式与一种第三方开源项目的使用
    Android Studio导入github下载的project和module
    SlidingMenu 侧滑菜单的用法
    显示图片的(自定义)吐司Toast
    用volley在Genymotion上获取网页源码
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6617562.html
Copyright © 2011-2022 走看看