[多项式] NTT

前置知识:FFT原根


书接上回。我们知道,FFT 的 \(\omega\) 作用特殊,我们找到了它的替代品:原根。

为什么原根符合呢?看看 \(\omega\) 的几条性质:

  • \(1.\) \(\omega_n^k=(\omega_n^1)^k\)
  • \(2.\) \(\omega_n^k\) 各不相同。
  • \(3.\) \(\omega_n^k=\omega_n^{k\bmod n}\)
  • \(4.\) \(\omega_n^k=-\omega_n^{k+n/2}\)
  • \(5.\) \(\omega_n^k=\omega_{2n}^{2k}\)

这些性质妙到复数域内找不到第二个数替代它。

我们知道,原根 \(g\) 的阶有 \(\varphi(p)\) 个数,并不是 \(n\) 个。
而对于 \(g^r\),阶数为 \(\dfrac{\varphi(p)}{\gcd(\varphi(p),r)}\)
又因为 \(p\) 是素数,所以阶数是 \(\dfrac{p-1}{\gcd(p-1,r)}\)
我们能发现,如果 \(n \nmid p-1\),那么是不存在一个数满足的。
我们知道,\(n=2^x\),所以我们希望 \(p=r\times2^x+1\)

假设我们取了这么好的原根质数 \(p\),我们想得到 \(n=\dfrac{p-1}{\gcd(p-1,r)}=\dfrac{p-1}{r}\),所以 \(r=\dfrac{p-1}{n}\)

我们发现 \(g_n^k\) 满足 \(1,2,3\)。我们看看它满不满足 \(4,5\)

  • 性质 \(4\) 需要满足:
    \(g_n^k+g_n^{k+n/2}\equiv0\Leftrightarrow g_n^k+g_n^k\times g_n^{n/2}\equiv 0\)
    现在我们证明 \(g_n^{n/2}\equiv -1\)
    我们知道 \(g_n^{n}\equiv 1\),所以 \(g_n^{n/2}\equiv \pm1\)
    由于这些数互不相同,所以\(g_n^{n/2}\equiv -1\)
    性质 \(4\) 得证。
  • 性质 \(5\)
    \(n\Rightarrow r_1=\dfrac{p-1}{n}\)
    \(2n\Rightarrow r_2=\dfrac{r_1}{2}\)
    \(g_n^k=g^{kr_1}\)
    \(g_{2n}^{2k}=g^{2kr_2}=g^{kr_1}\)
    至此,原根完美满足单位根所有性质。

所以,NTT 就是把\(\omega\) 变成原根。

#include<bits/stdc++.h>
#define ll long long
#define N 2000005
using namespace std;
const int G=3,iG=332748118;
const ll mod=998244353;
int Qpow(int a,int x)
{
	int s=1;
	while(x)
	{
		if(x&1) s=1ll*s*a%mod;
		a=1ll*a*a%mod;
		x>>=1;
	}
	return s;
}
int n,m;
int f[N*2],g[N*2];
int tr[N*2];
void NTT(int *f,int n,bool flag)
{
	for(int i=0;i<n;i++)
		if(i>tr[i]) swap(f[i],f[tr[i]]);
	for(int k=2;k<=n;k<<=1)
	{
		int len=(k>>1);
		int cur,w=Qpow(flag?G:iG,(mod-1)/k);
		for(int j=0;j<n;j+=k)
		{
			cur=1;
			for(int i=j;i<j+len;i++)
			{
				int tmp=1ll*cur*f[i+len]%mod;
				f[i+len]=f[i]-tmp;
				f[i]=f[i]+tmp;
				if(f[i+len]<0) f[i+len]+=mod;
				if(f[i]>mod) f[i]-=mod;
				cur=1ll*cur*w%mod;
			}
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<=n;i++) scanf("%d",&f[i]);
	for(int i=0;i<=m;i++) scanf("%d",&g[i]);
	for(m=n+m+1,n=1;n<m;n<<=1);
	for(int i=1;i<n;i++)
		tr[i]=(tr[i>>1]>>1)|((i&1)?n>>1:0);
	NTT(f,n,1),NTT(g,n,1);
	for(int i=0;i<n;i++) f[i]=1ll*f[i]*g[i]%mod;
	NTT(f,n,0);
	int invn=Qpow(n,mod-2);
	for(int i=0;i<m;i++) printf("%d ",1ll*f[i]*invn%mod);
	return 0;
}
posted @ 2024-05-08 15:20  g1ove  阅读(4)  评论(0编辑  收藏  举报