NTT

一、原根:
例题:51nod 1135 原根:

设m是正整数,a是整数,若a模m的阶等于φ(m),则称a为模m的一个原根。(其中φ(m)表示m的欧拉函数)
给出1个质数P,找出P最小的原根。
输入
输入1个质数P(3 <= P <= 10^9)
输出
输出P最小的原根。
输入样例
3
输出样例
2

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define ll long long
#define llu unsigned ll
#define int ll
using namespace std;
const int maxn=100100;
int prime[maxn],cnt=0;
bool ha[maxn];
int p[110],tot;
void Prime(void)
{
    ha[1]=true;
    for(int i=2;i<maxn;i++)
    {
        if(!ha[i]) prime[cnt++]=i;
        for(int j=0;j<cnt&&prime[j]*i<maxn;j++)
        {
            ha[i*prime[j]]=true;
            if(i%prime[j]==0) break;
        }
    }
}

bool mypow(int a,int b,int p)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans%p==1;
}

void only(int n)
{
    tot=0;
    for(int i=0;i<cnt&&prime[i]*prime[i]<=n;i++)
    {
        if(n%prime[i]) continue;
        p[++tot]=prime[i];
        while(n%prime[i]==0) n/=prime[i];
    }
    if(n>1) p[++tot]=n;
}

bool check(int g,int m)
{
    for(int i=1;i<=tot;i++)
    {
        if(mypow(g,(m-1)/p[i],m)) return false;
    }
    return true;
}

signed main(void)
{
    Prime();

    int n;
    scanf("%lld",&n);
    only(n-1);

    int pm=2;
    while(1)
    {
        if(check(pm,n))
            break;
        pm++;
    }
    printf("%lld\n",pm);
    return 0;
}

二、NTT:
P3803 【模板】多项式乘法(FFT):

题目背景
这是一道 FFT 模板题

题目描述
给定一个 n 次多项式 F(x),和一个 m 次多项式 G(x)。

请求出 F(x) 和 G(x) 的卷积。

输入格式
第一行 2 个正整数 n,m。

接下来一行 n+1 个数字,从低到高表示 F(x) 的系数。

接下来一行 m+1 个数字,从低到高表示 G(x) 的系数。

输出格式
一行 n+m+1 个数字,从低到高表示F(x)∗G(x) 的系数。

输入输出样例
输入 #1 复制
1 2
1 2
1 2 1
输出 #1 复制
1 4 5 2
说明/提示
保证输入中的系数大于等于 0 且小于等于 9。

对于100%的数据:n,m≤1e6。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=5e6+100;
const int p=998244353;
const int g=3;
int fi[maxn];
int a[maxn],b[maxn];

int mypow(int a,int b)
{
    if(b<0) return mypow(mypow(a,p-2),-b);
    int ans=1;
    while(b)
    {
        if(b&1) ans=1ll*ans*a%p;
        a=1ll*a*a%p;
        b>>=1;
    }
    return ans%p;
}

void ntt(int *x,int len,int f)
{
    for(int i=0;i<len;i++)
        if(i<fi[i]) swap(x[i],x[fi[i]]);

    for(int i=1;i<len;i<<=1)
    {
        int r=i<<1;
        int wn=mypow(g,f*(p-1)/r);
        for(int j=0;j<len;j+=r)
        {
            int w=1;
            for(int k=0;k<i;k++)
            {
                int xx=x[j+k],yy=1ll*w*x[j+i+k]%p;
                //实际上这里也有可能是负数
                x[j+k]=(xx+yy)%p;
                //若yy为负数,则有可能超int
                //x[j+i+k]=((xx-yy)%p+p)%p;
                //比较保险。
                x[j+i+k]=(xx-yy+p)%p;
                w=1ll*w*wn%p;
            }
        }
    }
    if(f==-1)
    {
        int invn=mypow(len,p-2);
        for(int i=0;i<len;i++)
            x[i]=1ll*x[i]*invn%p;
    }


}


int main(void)
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=0;i<=m;i++)
        scanf("%d",&b[i]);
    int len=1,cnt=0;
    while(len<=n+m) len<<=1,cnt++;
    for(int i=0;i<len;i++)
        fi[i]=((fi[i>>1]>>1)|((i&1)<<(cnt-1)));

    ntt(a,len,1);
    ntt(b,len,1);
    for(int i=0;i<len;i++)
        a[i]=1ll*a[i]*b[i]%p;
    ntt(a,len,-1);
    for(int i=0;i<=n+m;i++)
        printf("%d ",a[i]);
    return 0;

}

三、MTT:P4245 【模板】任意模数NTT:

题目背景
模板题,无背景

题目描述
给定 2 个多项式 F(x),G(x) ,请求出 F(x)∗G(x)。

系数对 p 取模,且不保证 p 可以分解成 p = a * 2^k + 1之形式。

输入格式
输入共 3 行。
第一行 3 个整数 n,m,p,分别表示 F(x),G(x) 的次数以及模数 p 。
第二行为 n+1 个整数, 第 i 个整数 ai表示 F(x) 的 i−1 次项的系数。
第三行为 m+1 个整数,第 i 个整数 bi表示 G(x) 的 i−1 次项的系数。

输出格式
输出n+m+1 个整数, 第 i 个整数 ci表示 F(x)∗G(x) 的 i−1 次项的系数。

输入输出样例
输入 #1 复制
5 8 28
19 32 0 182 99 95
77 54 15 3 98 66 21 20 38
输出 #1 复制
7 18 25 19 5 13 12 2 9 22 5 27 6 26
说明/提示
1≤n≤1e5,0≤ai,bi≤1e9,2≤p≤1e9+9

一般有两种解法:
①利用FFT:

//2.00s
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=4e5+100;
const double pi=acos(-1.0);
struct Complex
{
    double x,y;
    Complex(double xx=0.0,double yy=0.0)
    {
        x=xx,y=yy;
    }
    Complex operator - (const Complex &b) const
    {
        return Complex(x-b.x,y-b.y);
    }

    Complex operator + (const Complex &b) const
    {
        return Complex(x+b.x,y+b.y);
    }

    Complex operator * (const Complex &b) const
    {
        return Complex(x*b.x-y*b.y,x*b.y+y*b.x);
    }
};
int fi[maxn],ans[maxn];
int n,m,p;
Complex a1[maxn],b1[maxn],a2[maxn],b2[maxn],ww[maxn],a[maxn];


void fft(Complex *x,int len,int f)
{
    for(int i=0;i<len;i++)
        if(i<fi[i]) swap(x[i],x[fi[i]]);

    for(int i=1;i<len;i<<=1)
    {
        for(int r=i<<1,j=0;j<len;j+=r)
        {
            for(int k=0;k<i;k++)
            {
                Complex w=ww[len/i*k];
                w.y*=f;

                Complex xx=x[j+k],yy=w*x[j+i+k];
                x[j+k]=xx+yy;
                x[j+i+k]=xx-yy;
            }
        }
    }
    if(f==-1)
        for(int i=0;i<len;i++)
            x[i].x/=len;
}

void get(Complex *x,Complex *y,int len,int pm)
{
    for(int i=0;i<len;i++) a[i]=x[i]*y[i];
    fft(a,len,-1);
    for(int i=0;i<len;i++)
        ans[i]=(ans[i]+(ll)(a[i].x+0.5)%p*1ll*pm)%p;
}

int main(void)
{
    int x;
    scanf("%d%d%d",&n,&m,&p);
    int len=1,cnt=0;
    while(len<=n+m) len<<=1,cnt++;
    for(int i=0;i<len;i++)
    {
        fi[i]=((fi[i>>1]>>1)|((i&1)<<(cnt-1)));
        ww[i]=Complex(cos(pi/len*i),sin(pi/len*i));
    }

    int pm=32768;
    for(int i=0;i<=n;i++)
    {
        scanf("%d",&x);
        a1[i].x=x/pm,b1[i].x=x%pm;
    }
    for(int i=0;i<=m;i++)
    {
        scanf("%d",&x);
        a2[i].x=x/pm,b2[i].x=x%pm;
    }

    fft(a1,len,1);
    fft(b1,len,1);
    fft(a2,len,1);
    fft(b2,len,1);

    get(a1,a2,len,pm*pm%p);
    get(a1,b2,len,pm%p);
    get(a2,b1,len,pm%p);
    get(b1,b2,len,1);

    for(int i=0;i<=n+m;i++)
        printf("%d ",ans[i]);
    putchar('\n');
    return 0;

}

②利用NTT:

//5.66s
//常数有点大。。。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn=4e5+100;
const int p[3]={469762049,998244353,1004535809};
const int g=3;
int fi[maxn];
int a[3][maxn],b[3][maxn],ans[3][maxn];
int n,m,mod;



ll mypow(ll a,ll b,ll p)
{

    if(b<0) return mypow(mypow(a,p-2,p),-b,p);
    a%=p;
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ans%p;
}

void ntt(int *x,int len,int f,int p)
{
    for(int i=0;i<len;i++)
        if(i<fi[i]) swap(x[i],x[fi[i]]);

    for(int i=1;i<len;i<<=1)
    {
        int r=i<<1;
        int wn=mypow(g,f*(p-1)/r,p);
        for(int j=0;j<len;j+=r)
        {
            int w=1;
            for(int k=0;k<i;k++)
            {
                int xx=x[j+k],yy=1ll*w*x[j+i+k]%p;
                x[j+k]=(xx+yy)%p;
                x[j+i+k]=((xx-yy)%p+p)%p;
                w=1ll*w*wn%p;
            }
        }
    }
    if(f==-1)
    {
        int invn=mypow(len,p-2,p);
        for(int i=0;i<len;i++)
            x[i]=1ll*x[i]*invn%p;
    }


}

void get(int *x,int *y,int *ans,int len,int p)
{
    ntt(x,len,1,p);
    ntt(y,len,1,p);
    for(int i=0;i<len;i++) ans[i]=(ll)x[i]*y[i]%p;
    ntt(ans,len,-1,p);
}

int main(void)
{
    scanf("%d%d%d",&n,&m,&mod);
    int len=1,cnt=0;
    while(len<=n+m) len<<=1,cnt++;
    for(int i=0;i<len;i++)
        fi[i]=((fi[i>>1]>>1)|((i&1)<<(cnt-1)));

    for(int i=0;i<=n;i++)
    {
        scanf("%d",&a[0][i]);
        a[1][i]=a[2][i]=a[0][i];
    }

    for(int i=0;i<=m;i++)
    {
        scanf("%d",&b[0][i]);
        b[1][i]=b[2][i]=b[0][i];
    }


    for(int i=0;i<3;i++)
        get(a[i],b[i],ans[i],len,p[i]);

    int inv1=mypow(p[0],p[1]-2,p[1]);
    int inv2=mypow((ll)p[0]*p[1],p[2]-2,p[2]);
    for(int i=0;i<=n+m;i++)
    {
        ll x4=ans[0][i]+(((ans[1][i]-ans[0][i])%p[1]+p[1])%p[1]*1ll*inv1%p[1])*p[0];
        ll x=(x4%mod+(((ans[2][i]-x4)%p[2]+p[2])%p[2]*1ll*inv2%p[2])*p[0]%mod*p[1]%mod)%mod;
        x=(x%mod+mod)%mod;
        printf("%lld ",x);
    }
    return 0;

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值