NTT及原根学习笔记

原根

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

对p-1进行质因数分解 p1=ri=1prikii p − 1 = ∏ i = 1 r p r i i k i ,那么有

priigp1prii1 ∀ 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;
}

离散对数

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

NTT

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

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

当然如果p为奇数,那么2ceil(n/2)就会大于n,此时 p|p2 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+1rkg
3112
5122
17143
97355
193365
257183
768115917
1228931211
409615133
655371163
78643331810
576716911193
73400337203
2306867311213
10485760125223
1677721615253
4697620497263
998244353119233
1004535809479213
2013265921152731
228170137717273
32212254733305
7516192768135313
773094113299337
20615843020933622
206158430208115377
27487790694415393
65970697666573415
395824185999379425
791648371998739435
26388279066624115447
123145302310912135453
133700613937561719463
379991218559385727475
4222124650659841154819
78812993478983697506
315251973915934737523
1801439850948198415556
194555503902405427327565
417934045419982028929573
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值