NTT及原根学习笔记

原根

在模p意义下,满足 i [ 1 , p 1 ] g i ( mod p ) 任意两两不相同的最小正整数g为p的原根。

对p-1进行质因数分解 p 1 = i = 1 r p r i i k i ,那么有

p r i i g p 1 p r i i 1

下面是一个利用上述性质暴力求原根的方法,由于一般我们认为原根较小(详情可以见下附表),所以就可以暴力枚举咯

#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=100010;
int cnt,tot,pri[maxn],vis[maxn],stk[maxn];
ll n,tmp;
void init()
{
    for(int i=2;i<100000;i++)
    {
        if(!vis[i]) pri[++cnt]=i;
        for(int j=1;j<=cnt&&(ll)i*pri[j]<100000;j++)
        {
            vis[i*pri[j]]=1;
            if(i%pri[j]) break;
        }
    }
}
ll power(ll x,ll y,ll mod)
{
    ll res=1;
    for(;y;y>>=1,x=x*x%mod)
      if(y&1)
        res=res*x%mod;
    return res;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    init();
    scanf("%lld",&n);
    tmp=--n;
    for(int i=1;i<=cnt;i++)
      if(tmp%pri[i]==0)
      {
        stk[++tot]=pri[i];
        while(tmp%pri[i]==0) tmp/=pri[i];
      }
    if(tmp^1) stk[++tot]=tmp;
    for(int i=2,f;i<=n;i++)
    {
        f=1;
        for(int j=1;j<=tot&&f;j++)
          if(power(i,n/stk[j],n+1)==1)
            f=0;
        if(f){printf("%d\n",i);return 0;}
    }
    puts("Not found!");
    return 0;
}

离散对数

对于 g k x ( x < p ) ,我们称x的离散对数(亦称指标)为k,记做I(x)=k
它具有以下两个性质:
I ( a b ) I ( a ) + I ( b ) ( mod p 1 )
I ( a b ) b I ( a ) ( mod p 1 )

NTT

中文名叫快速数论变换,其实和FFT的用途是一样的,只不过由于FFT用到是复数,而且double在做了大量的实数运算之后精度损失较大,NTT就可以在模意义下,快速做这样的一个多项式乘法。好像NTT常数小一些
一般这个模数被认为是 r 2 k + 1 ,当然也有任意模数下的NTT ,但是我不会

看一道模板题多项式求逆
我们可以分治递归来做。我们记 x n = p x c e i l ( n / 2 ) = p ,记原多项式为A,模n意义下逆元为B,模n’意义下逆元为B’。
A B 1 ( mod p ) , A B 1 ( mod p )
由于p为p’的倍数,那么 A B 1 ( mod p )
由同余的一些性质我们得到 B B 0 ( mod p )
同时平方再展开 B 2 2 B B + B 2 0 ( mod p )
同乘A得 B 2 B + A B 2 0 ( mod p )
B 2 B A B 2 ( mod p )

当然如果p为奇数,那么2ceil(n/2)就会大于n,此时 p | p 2 ,式子同样成立

#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=2000010,mod=998244353,G=3;
int n,m,r[maxn],a[maxn],b[maxn],c[maxn];
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int power(int x,int y)
{
    int res=1;
    for(;y;y>>=1,x=(ll)x*x%mod)
      if(y&1)
        res=(ll)res*x%mod;
    return res;
}
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)
    {
        int gn=power(G,(mod-1)/(i<<1));
        for(int j=0;j<n;j+=(i<<1))
        {
            int x,y,g=1;
            for(int k=0;k<i;k++,g=(ll)g*gn%mod)
            {
                x=a[j+k],y=(ll)g*a[j+k+i]%mod;
                a[j+k]=pls(x,y);a[j+k+i]=dec(x,y);
            }
        }
    }
    if(f==-1)
    {
        int inv=power(n,mod-2);reverse(a+1,a+n);
        for(int i=0;i<n;i++) a[i]=(ll)a[i]*inv%mod;
    }
}
void work(int deg,int *a,int *b)
{
    if(deg==1){b[0]=power(a[0],mod-2);return ;}
    work((deg+1)>>1,a,b);
    int len=0,fn=1;
    while(fn<(deg<<1)) fn<<=1,len++;
    for(int i=1;i<fn;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
    for(int i=0;i<deg;i++) c[i]=a[i];
    for(int i=deg;i<fn;i++) c[i]=0;
    ntt(c,fn,1);ntt(b,fn,1);
    for(int i=0;i<fn;i++) b[i]=(ll)dec(2,(ll)c[i]*b[i]%mod)*b[i]%mod;
    ntt(b,fn,-1);
    for(int i=deg;i<fn;i++) b[i]=0;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    scanf("%d",&n);
    for(int i=0;i<=n;i++) scanf("%d",&a[i]);
    work(n,a,b);
    for(int i=0;i<n;i++) printf("%d ",b[i]);
    putchar('\n');
    return 0;
}

附表

r⋅2k+1 r k g
3 1 1 2
5 1 2 2
17 1 4 3
97 3 5 5
193 3 6 5
257 1 8 3
7681 15 9 17
12289 3 12 11
40961 5 13 3
65537 1 16 3
786433 3 18 10
5767169 11 19 3
7340033 7 20 3
23068673 11 21 3
104857601 25 22 3
167772161 5 25 3
469762049 7 26 3
998244353 119 23 3
1004535809 479 21 3
2013265921 15 27 31
2281701377 17 27 3
3221225473 3 30 5
75161927681 35 31 3
77309411329 9 33 7
206158430209 3 36 22
2061584302081 15 37 7
2748779069441 5 39 3
6597069766657 3 41 5
39582418599937 9 42 5
79164837199873 9 43 5
263882790666241 15 44 7
1231453023109121 35 45 3
1337006139375617 19 46 3
3799912185593857 27 47 5
4222124650659841 15 48 19
7881299347898369 7 50 6
31525197391593473 7 52 3
180143985094819841 5 55 6
1945555039024054273 27 56 5
4179340454199820289 29 57 3

猜你喜欢

转载自blog.csdn.net/as_a_kid/article/details/80503140