zoukankan      html  css  js  c++  java
  • 【模板】NTT

    NTT(快速数论变换)是一种更高效的计算多项式卷积的算法,具体优势体现在不涉及浮点数之间的运算,依靠取模操作完成与 FFT 相同的功能。
    NTT 利用了数论中原根和复数中单位根的四点相同的性质来进行对单位根运算的替代。
    具体来说,FFT 之所以具有十分优秀的复杂度,归根结底是由于单位根具备以下四点性质。

    1. (omega_{n}^i) 之间互不相同,这里保证的是点值选取的独立性。
    2. 蝴蝶操作性质1,(omega_{2n}^{2k}=omega_{n}^k),这里保证了分治策略的可行性。
    3. 蝴蝶操作性质2,(omega_{n}^{k+{nover 2}}=-omega_{n}^k),这里保证了分治策略的可行性。
    4. (sum_{i=0}^{n-1}omega_n^{ki}=0),这里保证了离散傅里叶逆变换的成立。

    对于 NTT 来说,可以发现数论中的原根同样满足以上四点性质,因此可以使用其替代单位根进行在模 (P) 意义下的运算。
    具体模数取作 (998244353=7*17*2^{23}+1),即:支持多项式项数在 (1e6) 级别的卷积计算。
    另外,在这个模数下,(3) 是其中的一个原根,(332748118)(3) 的逆元。

    代码如下

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int mod = 998244353;
    LL fpow(LL a, LL b) {
    	LL ret = 1 % mod;
    	for (; b; b >>= 1, a = a * a % mod) {
    		if (b & 1) {
    			ret = ret * a %mod;
    		}
    	}
    	return ret;
    }
    
    int main() {
    	int n, m;
    	scanf("%d %d", &n, &m);
    	int tot = 1, bit = 0;
    	while(tot <= n + m) {
    		tot <<= 1;
    		++bit;
    	}
    	vector<int> rev(tot);
    	for (int i = 0; i < tot; i++) {
    		rev[i] = rev[i >> 1] >> 1 | (i & 1) << bit - 1;
    	}
    	LL g = 3, ig = fpow(3, mod - 2);	
    	vector<LL> a(tot), b(tot);
    	for (int i = 0; i <= n; i++) {
    		scanf("%lld", &a[i]);
    	}
    	for (int i = 0; i <= m; i++) {
    		scanf("%lld", &b[i]);
    	}
    	auto ntt = [=](vector<LL> &v, int opt) {
    		for (int i = 0; i < tot; i++) {
    			if (i < rev[i]) {
    				swap(v[i], v[rev[i]]);
    			}
    		}
    		for (int mid = 1; mid < tot; mid <<= 1) {
    			LL wn = fpow(opt == 1 ? g : ig, (mod - 1) / (mid << 1)); 
    			for (int j = 0; j < tot; j += mid << 1) {
    				LL w = 1;
    				for (int k = 0; k < mid; k++) {
    					LL x = v[j + k], y = w * v[j + mid + k] % mod;
    					v[j + k] = (x + y) % mod, v[j + mid + k] = (x - y + mod) % mod;
    					w = w * wn % mod;
    				}
    			}
    		}
    		if (opt == -1) {
    			LL itot = fpow(tot, mod - 2);
    			for (int i = 0; i < tot; i++) {
    				v[i] = v[i] * itot % mod;
    			}
    		}
    	};
    	ntt(a, 1), ntt(b, 1);
    	for (int i = 0; i < tot; i++) {
    		a[i] = a[i] * b[i] % mod;
    	}
    	ntt(a, -1);
    	for (int i = 0; i <= n + m; i++) {
    		printf("%lld ", a[i]);
    	}
    	return 0;
    } 
    
  • 相关阅读:
    JBPM使用
    eclipse spring3.X redis 整合-配置
    30分钟学会如何使用Shiro
    Linux下环境变量配置方法梳理(.bash_profile和.bashrc的区别)
    Eclipse上Maven环境配置使用 (全)
    redis未设置idle超时时间导致连接过多
    linux下搭建redis并解决无法连接redis的问题
    redis配置用户认证密码
    spring配置redis
    Linux nohup和&后台运行,进程查看及终止,进程信息输出,控制台信息输出
  • 原文地址:https://www.cnblogs.com/wzj-xhjbk/p/10810476.html
Copyright © 2011-2022 走看看