[Wannafly挑战赛9]~F 导一导

本文详细介绍了数论变换(NTT)的基本原理及其应用,通过具体实例展示了如何利用快速幂逆元进行高效计算。同时,文章还探讨了整数范围内实现快速傅里叶变换(FFT)的方法。

话说: 这个题目真的蒙蔽,看了别人呢代码,心里还是七上八下 ,

需要快速数论变换NNT 加上快速幂 逆元 ,总之处理起来 很有趣

首先 我要恶补一下 NNT 的基础知识,这么多年的数字信号处理真的白学了,

首先感谢柳老师FFT ,虽然上课并没有听。但是FFT涉及小数,有可能有精度问题,所以这就很闹心了……发现精度问题出现在DFT和IDFT中求sin 和 cos 导致精度问题和IDFT中乘以(1/n) 的精度问题,所以我们考虑能否在整数范围内实现FFT的过程。
那么整数范围内的问题需要数论解决,这就导致了NTT(也叫FNT)的诞生。

====================>看到(1/n) 是不是要逆元 inv  |||||||||||||||||||||||||||||


<textarea readonly="readonly" name="cor" class="c++"> 
typedef long long LL;  
  
const int N = 1 << 18;  
const int P = (479 << 21) + 1;  
const int G = 3;  
const int NUM = 20;  
  
LL  wn[NUM];  
LL  a[N], b[N];  
char A[N], B[N];  
  
LL quick_mod(LL a, LL b, LL m)  
{  
    LL ans = 1;  
    a %= m;  
    while(b)  
    {  
        if(b & 1)  
        {  
            ans = ans * a % m;  
            b--;  
        }  
        b >>= 1;  
        a = a * a % m;  
    }  
    return ans;  
}  
  
void GetWn()  
{  
    for(int i=0; i<NUM; i++)  
    {  
        int t = 1 << i;  
        wn[i] = quick_mod(G, (P - 1) / t, P);  
    }  
}  
  
void Prepare(char A[], char B[], LL a[], LL b[], int &len)  
{  
    len = 1;  
    int len_A = strlen(A);  
    int len_B = strlen(B);  
    while(len <= 2 * len_A || len <= 2 * len_B) len <<= 1;  
    for(int i=0; i<len_A; i++)  
        A[len - 1 - i] = A[len_A - 1 - i];  
    for(int i=0; i<len - len_A; i++)  
        A[i] = '0';  
    for(int i=0; i<len_B; i++)  
        B[len - 1 - i] = B[len_B - 1 - i];  
    for(int i=0; i<len - len_B; i++)  
        B[i] = '0';  
    for(int i=0; i<len; i++)  
        a[len - 1 - i] = A[i] - '0';  
    for(int i=0; i<len; i++)  
        b[len - 1 - i] = B[i] - '0';  
}  
  
void Rader(LL a[], int len)  
{  
    int j = len >> 1;  
    for(int i=1; i<len-1; i++)  
    {  
        if(i < j) swap(a[i], a[j]);  
        int k = len >> 1;  
        while(j >= k)  
        {  
            j -= k;  
            k >>= 1;  
        }  
        if(j < k) j += k;  
    }  
}  
  
void NTT(LL a[], int len, int on)  
{  
    Rader(a, len);  
    int id = 0;  
    for(int h = 2; h <= len; h <<= 1)  
    {  
        id++;  
        for(int j = 0; j < len; j += h)  
        {  
            LL w = 1;  
            for(int k = j; k < j + h / 2; k++)  
            {  
                LL u = a[k] % P;  
                LL t = w * (a[k + h / 2] % P) % P;  
                a[k] = (u + t) % P;  
                a[k + h / 2] = ((u - t) % P + P) % P;  
                w = w * wn[id] % P;  
            }  
        }  
    }  
    if(on == -1)  
    {  
        for(int i = 1; i < len / 2; i++)  
            swap(a[i], a[len - i]);  
        LL Inv = quick_mod(len, P - 2, P);  
        for(int i = 0; i < len; i++)  
            a[i] = a[i] % P * Inv % P;  
    }  
}  
  
void Conv(LL a[], LL b[], int n)  
{  
    NTT(a, n, 1);  
    NTT(b, n, 1);  
    for(int i = 0; i < n; i++)  
        a[i] = a[i] * b[i] % P;  
    NTT(a, n, -1);  
}  
  
void Transfer(LL a[], int n)  
{  
    int t = 0;  
    for(int i = 0; i < n; i++)  
    {  
        a[i] += t;  
        if(a[i] > 9)  
        {  
            t = a[i] / 10;  
            a[i] %= 10;  
        }  
        else t = 0;  
    }  
}  
  
void Print(LL a[], int n)  
{  
    bool flag = 1;  
    for(int i = n - 1; i >= 0; i--)  
    {  
        if(a[i] != 0 && flag)  
        {  
            printf("%d", a[i]);  
            flag = 0;  
        }  
        else if(!flag)  
            printf("%d", a[i]);  
    }  
    puts("");  
}  
  
int main()  
{  
    GetWn();  
    while(scanf("%s%s", A, B)!=EOF)  
    {  
        int len;  
        Prepare(A, B, a, b, len);  
        Conv(a, b, len);  
        Transfer(a, len);  
        Print(a, len);  
    }  
    return 0;  
}  
</textarea>


 

题目描述

小M做到了一道这样的题
的n阶导。
小M当然不会啦,所以她向你请教

输入描述:

第一行两个正整数n,k,意义如题面所示
第二行k个整数ai

输出描述:

答案对998244353取模
第一行n+k个整数,其中第i个数表示n阶导中sin x/xi前的系数
第二行n+k个整数,其中第i个数表示n阶导中cos x/xi前的系数
示例1

输入

2 2
1 1

输出

998244352 998244352 2 6
0 998244351 998244349 0

备注:

n,k <= 100,000

0 <= ai < 998244353

代码是船长的,转载一下

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long LL; 


int fac[210000],inv[210000];
const int N = 1 << 19; 
const int P = 998244353; 
const int G = 3; 
const int NUM = 20; 
   
LL  wn[NUM]; 
LL  a[N], b[N]; 
char A[N], B[N]; 
   
LL quick_mod(LL a, LL b, LL m) 
{ 
    LL ans = 1; 
    a %= m; 
    while(b) 
    { 
        if(b & 1) 
        { 
            ans = ans * a % m; 
            b--; 
        } 
        b >>= 1; 
        a = a * a % m; 
    } 
    return ans; 
} 
void GetWn() 
{ 
    for(int i=0; i<NUM; i++) 
    { 
        int t = 1 << i; 
        wn[i] = quick_mod(G, (P - 1) / t, P); 
    } 
}    
void Prepare(char A[], char B[], LL a[], LL b[], int &len) 
{ 
    len = 1; 
    int len_A = strlen(A); 
    int len_B = strlen(B); 
    while(len <= 2 * len_A || len <= 2 * len_B) len <<= 1; 
    for(int i=0; i<len_A; i++) 
        A[len - 1 - i] = A[len_A - 1 - i]; 
    for(int i=0; i<len - len_A; i++) 
        A[i] = '0'; 
    for(int i=0; i<len_B; i++) 
        B[len - 1 - i] = B[len_B - 1 - i]; 
    for(int i=0; i<len - len_B; i++) 
        B[i] = '0'; 
    for(int i=0; i<len; i++) 
        a[len - 1 - i] = A[i] - '0'; 
    for(int i=0; i<len; i++) 
        b[len - 1 - i] = B[i] - '0'; 
}    
void Rader(LL a[], int len) 
{ 
    int j = len >> 1; 
    for(int i=1; i<len-1; i++) 
    { 
        if(i < j) swap(a[i], a[j]); 
        int k = len >> 1; 
        while(j >= k) 
        { 
            j -= k; 
            k >>= 1; 
        } 
        if(j < k) j += k; 
    } 
} 
void NTT(LL a[], int len, int on) 
{ 
    Rader(a, len); 
    int id = 0; 
    for(int h = 2; h <= len; h <<= 1) 
    { 
        id++; 
        for(int j = 0; j < len; j += h) 
        { 
            LL w = 1; 
            for(int k = j; k < j + h / 2; k++) 
            { 
                LL u = a[k] % P; 
                LL t = w * (a[k + h / 2] % P) % P; 
                a[k] = (u + t) % P; 
                a[k + h / 2] = ((u - t) % P + P) % P; 
                w = w * wn[id] % P; 
            } 
        } 
    } 
    if(on == -1) 
    { 
        for(int i = 1; i < len / 2; i++) 
            swap(a[i], a[len - i]); 
        LL Inv = quick_mod(len, P - 2, P); 
        for(int i = 0; i < len; i++) 
            a[i] = a[i] % P * Inv % P; 
    } 
} 
void Conv(LL a[], LL b[], int n) 
{ 
    NTT(a, n, 1); 
    NTT(b, n, 1); 
    for(int i = 0; i < n; i++) 
        a[i] = a[i] * b[i] % P; 
    NTT(a, n, -1); 
}

int w[110000],fs[2][110000];
int C(int n,int m)
{
    return 1LL*fac[n]*inv[m]%P*inv[n-m]%P;
}
int main()
{
    int n,k;
    GetWn();
    scanf("%d%d",&n,&k);
    int i,j,s,p,q,ip;
    for(i=0;i<=n+k;i++)
    {
        if(i==0)
           fac[i]=1;
        else
           fac[i]=1LL*fac[i-1]*i%P;
    }
    inv[n+k]=quick_mod(fac[n+k],P-2,P);
    for(i=n+k-1;i>=0;i--)
       inv[i]=1LL*inv[i+1]*(i+1)%P;

    for(i=0;i<k;i++)
       scanf("%d",&w[i]);
    memset(fs,0,sizeof(fs));
    for(i=0;i<=n;i++)
    {
        ip=0;
        if(n%2&&i%2==0)
           ip=1;
        if(n%2==0&&i%2)
           ip=1;
        fs[ip][i]=C(n,i);
        if(i%4==0&&(n%4==2||n%4==3))
           fs[ip][i]*=-1;
        else if(i%4==1&&(n%4==1||n%4==2))
           fs[ip][i]*=-1;
        else if(i%4==2&&(n%4==0||n%4==1))
           fs[ip][i]*=-1;
        else if(i%4==3&&(n%4==3||n%4==0))
           fs[ip][i]*=-1;
        if(fs[ip][i]<0)
           fs[ip][i]+=P;
    }

    int len=1;
    while(len<=(k+n+1))
        len<<=1;


    for(j=0;j<2;j++)
    {
       for(i=0;i<len;i++)
       {
          if(i<k)
            a[i]=1LL*w[i]*inv[i]%P;
          else
            a[i]=0;
       }
       for(i=0;i<len;i++)
       {
          if(i<=n)
             b[i]=fs[j][i];
          else
             b[i]=0;
        }
    
	   Conv(a,b,len);
       for(i=0;i<n+k;i++)
       {
            if(i)
               putchar(' ');
            printf("%d",1LL*a[i]*fac[i]%P);
       }
       printf("\n");
    }
    return 0;
}
未来的我一定会感谢正在努力的现在的我!




评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值