话说: 这个题目真的蒙蔽,看了别人呢代码,心里还是七上八下 ,
需要快速数论变换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当然不会啦,所以她向你请教
求
小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; }未来的我一定会感谢正在努力的现在的我!

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

被折叠的 条评论
为什么被折叠?



