首页 > 其他分享 >128MTT 学习笔记

128MTT 学习笔记

时间:2023-07-24 16:48:04浏览次数:42  
标签:return val m128 笔记 学习 constexpr 128MTT u128 MOD

标题是我乱起的名字

在做某题时受到了启发,想出了一种之前没听说过的 MTT,在某谷上一问发现有人和我想的一样,立马去学了。

这种方法,我叫它 128MTT,它用到了科技 __int128。主要思想就是找一个 \(10^{27}\) 以上的大 NTT 模数,全程使用 __int128 做 NTT。

然而 long long 取模尚能用 __int128__int128 自己又怎么快速模乘呢?解锁新科技:Montgomery 模乘。

这样,我们就可以做一次 NTT 了,吊打各种 \(3\) 次 \(4\) 次的 FFT。(bushi)

这样,我们就可以类 NTT 来做 MTT 了。原理与 NTT 完全相同。

关于 NTT 模数:

for i in range(1000):
    if miller_rabin(2**76*i+1): print(i, 2**76*i+1)
58 4382356096103030758309889
60 4533471823554859405148161
93 7026881326510032077979649
100 7555786372591432341913601
145 10955890240257576895774721
151 11409237422613062836289537
235 17756097975589866003496961
286 21609549025611496497872897
288 21760664753063325144711169
348 26294136576618184549859329
352 26596368031521841843535873
385 29089777534477014516367361
441 33321017903128216627838977
442 33396575766854130951258113
466 35209964496276074713317377
502 37930047590408990356406273
531 40121225638460505735561217
547 41330151458075134910267393
582 43974676688482136229937153
603 45561391826726337021739009
651 49188169285570224545857537
795 60068501662101887118213121
813 61428543209168344939757569
832 62864142619960717084721153
835 63090816211138460054978561
841 63544163393493945995493377
861 65055320668012232463876097
972 73442243541588722363400193
988 74651169361203351538106369
993 75028958679832923155202049

其中 Miller-Rabin 是从这里抄来的。

推荐 \(100 \times 2^{76}+1\) 和 \(502 \times 2^{76}+1\),它们的最小原根都是 \(3\),且好记。我用的是 \(100 \times 2^{76}+1\).

例题:P4245

#include <iostream>
#include <fstream>
#include <cassert>
#include <algorithm>
#include <vector>
#define UP(i,s,e) for(auto i=s; i!=e; ++i)
using std::cin;
using std::cout;
namespace BITS{ // }{{{
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128_t;
using u128 = __uint128_t;
struct u256{
	u128 hi, lo;
	constexpr u256(u128 hi, u128 lo):hi(hi), lo(lo){}
	constexpr u256 &operator=(u256 x){ hi=x.hi, lo=x.lo; return *this; }
	constexpr u256 operator+=(u256 x){ 
		if(((lo>>1)+(x.lo>>1)+(lo&1&x.lo)) >> 63) hi++;
		hi += x.hi; lo += x.lo;
		return *this;
	}
};
constexpr u256 mul128(u128 x, u128 y){
	u64 xl(x), xh(x>>64), yl(y), yh(y>>64);
	u128 mid(u128(xl)*yh), anslo(u128(xl)*yl);
	u64 mh(mid>>64), ml(mid);
	mid = u128(yl)*xh + ml + (anslo >> 64);
	return u256(u128(xh)*yh + mh + (mid>>64), 
			(mid<<64) | u64(anslo));
}
constexpr u128 str_to_u128(char const *s){
	u128 x(0);
	while(*s){ x = x*10+(*s)-'0'; s++; }
	return x;
}
char* u128_to_string(u128 x){ // u128 < 10**39
	char buf[40]={0}, *p = buf+38;
	do { 
		*p = x%10 + '0';
		x/=10;
		p--;
	} while(x>0);
	return p+1;
}
} // {}}}
BITS::u64 gethi(BITS::u128 x){ return x>>64; }
BITS::u64 getlo(BITS::u128 x){ return x; }
template<typename T>
constexpr T exgcd(T a, T b, T &x, T &y){
	if(b == 0) return x=1, y=0, void();
	exgcd(b, a%b, y, x); y-=a/b*x;
}
template<typename T>
constexpr T qpow(T x, BITS::u128 tim){
	T a(1);
	while(tim){
		if(tim&1) a*=x;
		x*=x;
		tim>>=1;
	}
	return a;
}
namespace Montgomery{ // }{{{
using namespace BITS;
constexpr u128 MOD = str_to_u128("7555786372591432341913601"); // MOD must be < 2**63
constexpr int MOD_G = 3;
constexpr u128 NIMOD = -qpow(MOD, (u128(1)<<127)-1);
constexpr int lgR = 128;
constexpr u128 reduce(u256 x){
	x += mul128(x.lo*NIMOD, MOD); // mod 2**lgR
	assert(x.lo == 0);
	return x.hi >= MOD ? x.hi-MOD : x.hi;
}
constexpr u128 R1 = reduce(mul128((-MOD)%MOD, (-MOD)%MOD));
constexpr u128 R2_(){
	u256 ans = mul128(R1, R1); u128 anslo(ans.lo%MOD);
	while(ans.hi > 0){
		ans.hi %= MOD;
		ans = mul128(ans.hi, R1);
		anslo += ans.lo%MOD;
		anslo %= MOD;
	}
	return anslo;
}
constexpr u128 R2 = R2_();
constexpr u128 R3 = reduce(mul128(R2, R2));
constexpr u128 mmul(u128 x, u128 y){ return reduce(mul128(x,y)); }
class m128{ // Montgomery unsigned 128 bit modulo MOD
	private:
	u128 val;
	public:
	m128(){}
	m128 &operator=(m128 x){ val = x.val; return *this; }
	m128 &operator=(u128 x){ val = mmul(x, R2); return *this; }
	m128(u128 x){ *this = x; }
	m128 &operator+=(m128 x){ val += x.val; val = val>=MOD ? val-MOD :val; return *this; }
	m128 operator+(m128 x){ x+=*this; return x; }
	m128 &operator-=(m128 x){ if(val < x.val) val+=MOD; val -= x.val; return *this; }
	m128 operator-(m128 x){ m128 t=*this; t-=x; return t; }
	m128 &operator*=(m128 x){ val = mmul(val, x.val); return *this; }
	m128 operator*(m128 x){ x*=*this; return x; }
	u128 get(){ return mmul(val, 1); }
};
} // {}}}
namespace Poly{ // }{{{
using CC = Montgomery::m128;
using Montgomery::MOD_G;
using Montgomery::MOD;
void change(CC *y, int len){
	static std::vector<int> rev; static int llen = 0;
	if(llen != len){
		llen = len;
		rev.reserve(len);
		rev[0] = 0;
		UP(i, 1, len){
			rev[i] = rev[i>>1] >> 1;
			if(i&1) rev[i] |= len>>1;
		}
	}
	UP(i, 0, len) if(i<rev[i]) std::swap(y[i], y[rev[i]]);
}
void ntt(CC *y, int len, bool idft){
	change(y, len);
	for(int h=2; h<=len; h<<=1){
		CC wn = qpow(CC(MOD_G), (MOD-1)/h);
		for(int j=0; j<len; j+=h){
			CC w(1);
			UP(k, j, j+h/2){
				CC u = y[k], v = w * y[k+h/2];
				y[k] = u+v, y[k+h/2] = u-v;
				w = w * wn;
			}
		}
	}
	if(idft){
		std::reverse(y+1, y+len);
		CC invlen = qpow(CC(len), MOD-2);
		UP(i, 0, len){
			y[i] *= invlen;
		}
	}
}
void dot(CC *y, CC *z, int len){ UP(i, 0, len) y[i] *= z[i]; }
void polymul(CC *y, CC *z, int len){
	ntt(y, len, false);
	ntt(z, len, false);
	dot(y, z, len);
	ntt(y, len, true);
}
} // {}}}
namespace m{ // }{{{
int in, im, ip;
Poly::CC ia[1<<18], ib[1<<18];
void work(){
	cin >> in >> im >> ip;
	UP(i, 0, in+1){
		int x;
		cin >> x;
		ia[i] = x;
	}
	UP(i, 0, im+1){
		int x;
		cin >> x;
		ib[i] = x;
	}
	int len = 1;
	while(len < im+in+1) len<<=1;
	Poly::polymul(ia, ib, len);
	UP(i, 0, in+im+1){
		cout << BITS::u64(ia[i].get()%ip) << ' ';
	}
}
} // {}}}
int main(){
#if ONLINE_JUDGE
	std::ios::sync_with_stdio(0); std::cin.tie(0);
#endif
   	m::work(); return 0; 
}

跑的比这篇慢多了,果然人傻常大 =(

标签:return,val,m128,笔记,学习,constexpr,128MTT,u128,MOD
From: https://www.cnblogs.com/x383494/p/17577595.html

相关文章

  • Blazor学习之旅(5)数据绑定
    大家好,我是Edison。本篇,我们来了解下在Blazor中数据是如何绑定的。关于数据绑定如果希望HTML元素显示值,可以编写代码来更改显示内容。如果值发生更改,则需要编写额外的代码以更新显示内容。在Blazor中,可以使用数据绑定将HTML元素连接到字段、属性或表达式。这样,当值发生......
  • VUEX笔记
    VUEX笔记statestate:{ ip:''}gettersconstgetters={ ip:state=>state.ip}mutations同步操作this.$store.commit()mutations:{ SET_IP:(state,ip)=>{ state.ip=ip }}actions异步操作this.$store.dispatch()Action类似于mutati......
  • TED Talk 学习笔记
    Howtospeaksothatpeoplewanttolisten|JulianTreasureAvoid:gossipjudgingnegativitycomplaning:viralmiseryexcuseslying:embroidery,exaggerationdogmatism:bombardsomebodyCornerstones/Foundations:HAIL,togreetoracclaimenthusiasitcal......
  • 爬虫 | Python爬虫应该学习什么知识点?
    什么是爬虫如果说把互联网比喻成蜘蛛网,那么爬虫就是在这张网上的蜘蛛,它可以在上面爬来爬去。在互联网中,爬虫就是机器人,你应该对百度和Google很熟悉吧,为什么我们可以很快的从它们的搜索引擎中获取到资料呢?原因就是它们都有自己的爬虫,在整个互联网上,24小时不间断的爬取那些愿意......
  • Lombok笔记
    Lombok项目是一个java库,它可以自动插入到编辑器和构建工具中,增强java的性能。不需要再写getter、setter或equals方法,只要有一个注解,就有一个功能齐全的构建器、自动记录变量等等。使用步骤:1:安装插件2:导入架包<dependencies><dependency><groupId>org.projectlomb......
  • AirNet使用笔记8
    摘要:SDD显示多监视源航迹;1、SDD同时显示多监视源航迹,在“DataSource”选择。.sdd_offline.conf.0不加点不是隐藏文件也行。[root@ACC-3conf]#more/home/cdatc/AirNet/bin/conf/.sdd_offline.conf.0B_OPS_IS_MAINTAIN=1......
  • rodert单排学习redis进阶【青铜】
    redis之青铜image.png[toc]前言声明:参考来源互联网,有任何争议可以留言。站在前人的肩上,我们才能看的更远。本教程纯手打,致力于最实用教程,不需要什么奖励,只希望多多转发支持。欢迎来我公众号,希望可以结识你,也可以催更,微信搜索:JavaPub有任何问题都可以来谈谈![图片上传失......
  • C#中的重写与多态知识点整理(刘铁锰老师课堂笔记)
    在C#中,重写(Override)和多态(Polymorphism)是面向对象编程中的重要概念。通过重写和多态,我们可以更好地组织和管理代码,提高代码的可维护性和可扩展性。重写(Override)重写是指在派生类中重新实现基类中已经定义的方法。通过重写一个方法,我们可以为派生类中的该方法提供新的实现,同时让......
  • ZLMediaKit学习记录--RTMP
    第二次看ZLMediaKit部分的记录,有许多理解错误的地方,将进一步整理。cd/opt/ZLMediaKitmkdirbuildcdbuildcmake..-DENABLE_WEBRTC=true-DOPENSSL_ROOT_DIR=/opt/openssl-DOPENSSL_LIBRARIES=/opt/openssl/libcmake--build.--targetMediaServercd/opt/ZLMediaKit/r......
  • 【js学习笔记五十二】weakmap的应用
     目录前言导语 代码部分前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷导语WeakuMap编辑 代码......