首页 > 其他分享 >MTT 小记

MTT 小记

时间:2024-07-05 20:22:18浏览次数:9  
标签:pre pmod long MTT equiv mod include 小记

有的时候,我们想要让多项式乘法结果中的系数,对一些不是那么常规的模数取模,或者对任意质数 \(p\) 取模,这时候我们可以用 MTT 来解决。

MTT 有两种,一种是三模数 NTT,另一种是拆系数 FFT。

  • 三模数 NTT

三模数 NTT 就是,选择三个著名的 NTT 模数,比如 998244353、1004535809、469762049,它们的原根都是 3。然后我们求出模它们意义下的答案,接着 CRT 合并。

具体地,如果我们求出了 \(x \equiv x_1 \pmod A, x \equiv x_2 \pmod B, x \equiv x_3 \pmod C\)。

那么有 \(x_1 + k_1A = x_2 + k_2B\)。则可以解得 \(k_1 = \frac{x_2 - x_1}{A} \pmod B\)。

于是我们把 \(k_1\) 代回 \(x \equiv x_1 + k_1A\) 中可以得到 \(x \equiv x_4 \pmod{AB}\)。

然后我们用类似的方法,有 \(x_4 + k_4AB = x_3 + k_3C\)。

解得 \(k_4 = \frac{x_3 - x_4}{AB} \pmod C\),代回来可以得到 \(x \equiv x_5 \pmod{ABC}\)。

由于 \(ABC\) 大于真实值,所以 \(x_5 \mod p\) 即为答案。

  • 拆系数 FFT

考虑让 \(P(x) = tA(x) + B(x), Q(x) = tC(x) + D(x)\),其中 \(A(x), C(x)\) 为除 \(t\) 后的商,\(B(x), D(x)\) 为除 \(t\) 后的余数。

则 \(P(x)Q(x) = (tA(x) + B(x))(tC(x) + D(x))\)。

拆开以后大力算一下就行,其中 \(t\) 一般取 \(\sqrt p\) 级别的二的正整数次幂。

好像用个什么共轭优化就可以只需要 4 次变换,比朴素的 7 次要优很多。

  • 代码实现(三模数 NTT)
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define maxN 100010
const long long g = 3;
const long long mod[3] = {998244353, 1004535809, 469762049};
struct Poly{ long long a[maxN << 2]; long long N; } P, Q;
long long rev[maxN << 2];
long long read ()
{
	long long x = 0, Fu = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') Fu = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = x * 10 + (c - '0');
		c = getchar();
	}
	return x * Fu;
}
void Swap (long long &x, long long &y) { long long t = x; x = y; y = t; }
long long Abs (long long x) { return (x >= 0) ? x : (-x); }
long long Min (long long x, long long y) { return x < y ? x : y; }
long long Max (long long x, long long y) { return x > y ? x : y; }
long long Pow (long long x, long long y, long long pre)
{
	if(!y) return 1;
	long long res = Pow(x, y >> 1, pre);
	if(!(y & 1)) return res * res % mod[pre];
	else return res * res % mod[pre] * x % mod[pre];
}
void NTT (Poly &A, long long typ, long long Limit, long long pre)
{
	long long G = Pow(g, mod[pre] - 2, pre);
	for(long long i = 0;i < Limit; i++)
		if(i < rev[i]) Swap(A.a[i], A.a[rev[i]]);
	for(long long mid = 1;mid < Limit;mid <<= 1)
	{
		long long Wn = Pow((typ == 1) ? g : G, (mod[pre] - 1) / (mid << 1), pre);
		for(long long j = 0;j < Limit;j += (mid << 1))
		{
			long long w = 1;
			for(long long k = 0;k < mid; k++, w = w * Wn % mod[pre])
			{
				long long x = A.a[j + k], y = A.a[j + mid + k] * w % mod[pre];
				A.a[j + k] = (x + y) % mod[pre]; A.a[j + mid + k] = (x - y + mod[pre]) % mod[pre];
			}
		}
	}
}
Poly NTT_Multi (Poly P, Poly Q, long long pre)
{
	long long N = P.N, M = Q.N; long long Len = N + M;
	long long Limit = 1, l = 0; while(Limit <= N + M) Limit <<= 1, l++;
	for(long long i = 0;i < Limit; i++)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
	NTT(P, 1, Limit, pre); NTT(Q, 1, Limit, pre);
	for(long long i = 0;i < Limit; i++)
		P.a[i] = P.a[i] * Q.a[i] % mod[pre];
	NTT(P, -1, Limit, pre);
	long long inv_N = Pow(Limit, mod[pre] - 2, pre);
	P.N = Len;
	for(long long i = 0;i <= P.N; i++)
		P.a[i] = P.a[i] * inv_N % mod[pre];
	return P;
}
int main ()
{
	P.N = read(); Q.N = read(); long long p = read();
	for(long long i = 0;i <= P.N; i++) P.a[i] = read() % p;
	for(long long i = 0;i <= Q.N; i++) Q.a[i] = read() % p;
	Poly G1 = NTT_Multi(P, Q, 0);
	Poly G2 = NTT_Multi(P, Q, 1);
	Poly G3 = NTT_Multi(P, Q, 2);
	long long A = mod[0], B = mod[1], C = mod[2];
	for(long long i = 0;i <= P.N + Q.N; i++)
	{
		long long x1 = G1.a[i], x2 = G2.a[i], x3 = G3.a[i];
		long long k1 = (x2 - x1 + B) % B * Pow(A, B - 2, 1) % B;
		long long x4 = (x1 + k1 * A);
		long long k4 = (x3 - (x4 % C) + C) % C * Pow(A * B % C, C - 2, 2) % C;
		printf("%lld ", (x4 + k4 * A % p * B) % p);
	}
	return 0;
}

标签:pre,pmod,long,MTT,equiv,mod,include,小记
From: https://www.cnblogs.com/abcdeffa/p/18286521

相关文章

  • 【python小记】使用openpyxl库在同一个工作表下复制单元格(包括它们的值、样式和合并属
    fromopenpyxlimportload_workbook#加载工作簿和工作表wb=load_workbook('test.xlsx')sheet=wb['sheet1']#定义一个函数来复制样式defcopy_style(source_cell,target_cell):ifsource_cell.has_style:target_cell.font=source_cell.font.co......
  • 六月小记
    这个月身体挺疲惫的,nemu的进度也不是特别好,到北京还需要面对租房和学校的琐事,希望7月会好起来。过去一个月在做nemu时候发现函数指针掌握的并不是特别好,索性重新复习下。函数指针声明:typedefint(*fun_ptr)(int,int);//声明一个指向同样参数、返回值的函数指针类型#inclu......
  • 2024.7.1 之后的做题小记
    7.1P7124[Ynoi2008]stcm维护一个\(O(n\logn)\)级别的子树补不删除莫队。Solution1:考虑菊花图,忽略根节点,一个显然的做法是把这些节点扔进线段树,然后遍历某个节点时候就把它的兄弟节点内所有点加进来。这个做法是线段树所有节点大小和即\(O(n\logn)\)。然后在一条链上......
  • 博弈论小记
    博弈论目录博弈论公平组合游戏\(N/P\)\(SG\)函数\(SG\)和Nim游戏EasyGameTakeAwayHungergameStaircaseLasker'sNim翻硬币问题例题P4363[九省联考2018]一双木棋chess题目描述solutionP5363[SDOI2019]移动金币题目大意solutionP3185[HNOI2007]分裂游戏题目大意solution博......
  • CF VP小记
    目录CF1956FNeneandthePassingGame题目大意solutionCF1942EFarmGame题目大意solutionCF1942GBessieandCards题目大意solutiontipsCF1943D2题目大意solutionE3.Trails题目大意solutionCF1956FNeneandthePassingGame题目大意给定\(n\)个点,每个点有两个系数\([......
  • java小记-随机数、数组
    练习4:①随机数:类似scanner键盘录入的三步:答:(只能猜一次)如果继续猜呢:添加循环:注意:添加新的功能:保底,抽的次数到某个时刻,直接猜中,不管结果几何。②数组:......
  • QEMU + Vscode + Arm Arch's Linux调试小记
    QEMU+Vscode+ArmArch'sLinux调试小记​ 前几天看到了一篇讲授如何调试ARMLinux内核的文章,这里现在记录一下调试ARMLinux内核的办法下载QEMU​ 对于ArchLinux用户而言,没有必要自己编译,直接上AUR源下载就行。我自己有打算研究和调试多个架构,所以我自己下载了:yay-Sqem......
  • git submodule小记
    这是一篇记录gitsubmodule中存在的坑的文档引用一个模块的命令gitsubmoduleaddhttp://your-submodule-url.com/local/path这个命令可以将一个子模块添加到当前的主仓库中(注意,这样添加的是最新版的) 这个gitsubmodule有一些坑爹的地方当你本地添加了一个子模块后,一旦......
  • React小记(二)_组件通信、生命周期、hooks等
    10、组件通信(父=>子)10.1基本使用1、传递方式与函数组件一致2、接收时通过this.props.mes获取importReactfrom'react'classSonextendsReact.PureComponent{render(){return(<><h3>子组件</h3>{/*2、接收*/}......
  • 开源组件小记
    分布式ID生成服务:leaf监控:cat实时应用监控平台配置中心:apolloJAVA诊断工具:arthas数据库连接池:druid消息中间件:rocketmq服务注册中心:nacos动态服务发现、配置和服务管理而设计。它可以帮助您轻松构建云原生应用程序和微服务平台服务治理:S......