zoukankan      html  css  js  c++  java
  • 【luogu P3803】【模板】多项式乘法(NTT)

    【模板】多项式乘法(NTT)

    题目链接:luogu P3803

    题目大意

    给你两个多项式,要你求它们的卷积。

    思路

    这次我们写 NTT 的做法。
    它的优点就是它可以取模,而且不会有精度问题,而且会比 FFT 稍微快一点?

    然而它的确定就是它的模数比较死板,一般就 (998244353,1004535809,469762049) 这些。(它们的原根都是 (3)

    其实就是考虑用原根代替复数。
    因为它是跟单位根有着相同的性质。

    然后通过证明,我们可以得到 (omega_nequiv g^{frac{p-1}{n}}mod p)
    然后你就把 FFT 中的 (omega_i) 都换掉就是 NTT 了。
    然后这里我 (p) 取的是 (998244353),原根是 (3)

    代码

    #include<cstdio>
    #include<algorithm>
    #define ll long long
    #define mo 998244353
    #define G 3
    
    using namespace std;
    
    int n, m, an[4000001];
    int limit, ln;
    ll a[4000001], b[4000001], Gv;
    
    ll ksm(ll x, ll y) {
    	ll re = 1;
    	while (y) {
    		if (y & 1) re = (re * x) % mo;
    		x = (x * x) % mo;
    		y >>= 1;
    	}
    	return re;
    }
    
    void NTT(ll *now, int op) {
    	for (int i = 0; i < limit; i++)
    		if (i < an[i]) swap(now[i], now[an[i]]);
    	
    	for (int mid = 1; mid < limit; mid <<= 1) {
    		ll Wn = ksm(op == 1 ? G : Gv, (mo - 1) / (mid << 1));
    		//如果这里是负的那就是逆元的次方
    		for (int R = (mid << 1), j = 0; j < limit; j += R) {
    			ll w = 1;
    			for (int k = 0; k < mid; k++, w = (w * Wn) % mo) {
    				ll x = now[j + k], y = w * now[j + mid + k] % mo;
    				now[j + k] = (x + y) % mo;
    				now[j + mid + k] = (x - y + mo) % mo;
    			}
    		}
    	}
    }
    
    int main() {
    	scanf("%d %d", &n, &m);
    	for (int i = 0; i <= n; i++) {
    		scanf("%d", &a[i]);
    		a[i] = (a[i] % mo + mo) % mo;
    	}
    	for (int i = 0; i <= m; i++) {
    		scanf("%d", &b[i]);
    		b[i] = (b[i] % mo + mo) % mo;
    	}
    	
    	limit = 1;
    	while (limit <= n + m) {
    		limit <<= 1; ln++;
    	}
    	for (int i = 0; i < limit; i++)
    		an[i] = (an[i >> 1] >> 1) | ((i & 1) << (ln - 1));
    	Gv = ksm(G, mo - 2);
    	
    	NTT(a, 1);
    	NTT(b, 1);
    	for (int i = 0; i < limit; i++)
    		a[i] = (a[i] * b[i]) % mo;
    	NTT(a, -1);
    	
    	ll liv = ksm(limit, mo - 2);//除就变成逆元乘
    	for (int i = 0; i <= n + m; i++)
    		printf("%lld ", a[i] * liv % mo);
    	
    	return 0;
    }
    
  • 相关阅读:
    ruby 正则表达式 匹配中文
    ruby 正则表达式 匹配所有符合规则的信息
    MobileNetV2: Inverted Residuals and Linear Bottlenecks
    MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications
    SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size
    LeetCode 409——最长回文串
    LeetCode 516——最长回文子序列
    LeetCode 5——最长回文子串
    LeetCode 300——最长上升子序列
    动态规划之——最长公共子串和矩阵链乘法
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P3803_NTT.html
Copyright © 2011-2022 走看看