1. 程式人生 > >NTT任意模數模板(+O(1)快速乘)

NTT任意模數模板(+O(1)快速乘)

NTT任意模數的方法其實有點取巧。

兩個數列每個有n個數,每個數的大小最多是10^9。

如果沒有模數,那麼卷積過後每個位置的答案一定小於10^9*10^9*n,差不多是10^24左右

那麼就有一個神奇的做法,選3個乘積大於10^24的NTT模數,分別做一次,得到每個位上模意義下的答案,

然後用中國剩餘定理得到模上三個質數乘積的答案。

因為答案顯然小於三個質數乘積,那麼模上三個質數乘積的答案就是這個數應該的值。

不過這個值可能會超long long(及時不超,對於乘積大於long long的三個質數做中國剩餘定理也不是一件小事)

考慮先將兩個模數意義下的答案合併,

現在我們還剩兩個模數,一個為long long,一個為int

不能中國剩餘定理硬上了。

設模數為P1(longlong) ,P2(int), 餘數為a1,a2

設答案ANS=P1*K+a1=P2*Q+a2

那麼K*P1=P2*Q+(a2-a1)

K*P1 % P2=a2-a1

a1-a2為常數

用同餘方程的解法即可解出K模P2(int)意義下的值

又有ANS<P1*P2(之前已證)

so K*P1+a1<P1*P2

顯然K<P2

所以原本答案K的值只能為模P2意義下的值

所以我們就求出K了,然後可以不用高精度就算出ANS%MOD(MOD為任意模數)

但是,

回顧整個過程,附加條件非常多。。。。。。

首先每個數<=10^9(再大或許可以通過增加模數的方法解決,但是CRT時可就不能迴避高精度取模了,常數捉急)

然後K的值必須為非負.(如果為負數那麼就有兩個可能的答案了,這是你用第一條性質怎樣都無法迴避的)

其次你需要解決兩個long long相乘mod long long

用二分乘法會T(常數啊),可以用接近作弊的O(1)long long乘法取模:

//O(1)快速乘
LL mul(LL a,LL b,LL P)
{
	a=(a%P+P)%P,b=(b%P+P)%P;
	return ((a*b-(LL)((long double)a/P*b+1e-6)*P)%P+P)%P;
}

主要原理是mod後的答案用公式 A % B=A-floor(A/B)*B來算。

注意其中A和floor(A/B)*B都是可能爆long long的。

但是因為減法,所以無論是兩個都不溢位還是兩個都溢位亦或是一個溢位另一個不溢位,都沒有關係。。。。。。。

(好像2009集訓隊論文中關於底層優化的那篇上有)

模板:

#include<cstdio>
#include<cstring>
#include<cctype>
//#include<ctime>
#include<algorithm>
#define maxn 300005
#define LL long long
#define RealMod 1000000007
using namespace std;

int n,m;
int P[3]={998244353,1004535809,469762049},G[3]={3,3,3},invG[3],wn[3][2][maxn];

inline int pow(int base,int k,int P)
{
	int ret=1;
	for(;k;k>>=1,base=1ll*base*base%P) if(k&1) ret=1ll*ret*base%P;
	return ret;
}

inline void Prework(int id)
{
	invG[id]=pow(G[id],P[id]-2,P[id]);
	for(int i=1;i<24;i++) wn[id][1][i]=pow(G[id],(P[id]-1)/(1<<i),P[id]),wn[id][0][i]=pow(invG[id],(P[id]-1)/(1<<i),P[id]);
}

inline void NTT(int *A,int n,int typ,int id)
{
	for(int i=0,j=0,k;i<n;i++)
	{
		if(i<j) swap(A[i],A[j]);
		for(k=n>>1;k;k>>=1) if((j^=k)>=k) break;
	}

	for(int i=1,j,k,len,w,x,y;1<<i<=n;i++)	
	{
		len=1<<(i-1);
		for(j=0,w=1;j<n;j+=1<<i,w=1)
			for(k=0;k<len;k++,w=1ll*w*wn[id][typ][i]%P[id])
			{
				x=A[j+k],y=1ll*A[j+k+len]*w%P[id];
				A[j+k]=(x+y)%P[id];
				A[j+k+len]=(x-y+P[id])%P[id];
			}
	}
	
	if(typ==0)
		for(int i=0,inv=pow(n,P[id]-2,P[id]);i<n;i++)
			A[i]=1ll*A[i]*inv%P[id];
}

void mul(int *ret,int *A,int lena,int *B,int lenb,int id)
{
	static int seq1[maxn],seq2[maxn];
	int n=1;for(;n<=lena+lenb;n<<=1);
	for(int i=0;i<n;i++) 
	{
		if(i<=lena) seq1[i]=A[i]; else seq1[i]=0;
		if(i<=lenb) seq2[i]=B[i]; else seq2[i]=0;
	}
	NTT(seq1,n,1,id);NTT(seq2,n,1,id);
	for(int i=0;i<n;i++) ret[i]=1ll*seq1[i]*seq2[i]%P[id];
	NTT(ret,n,0,id);
}

//O(1)快速乘
LL mul(LL a,LL b,LL P)
{
	a=(a%P+P)%P,b=(b%P+P)%P;
	return ((a*b-(LL)((long double)a/P*b+1e-6)*P)%P+P)%P;
}
/*
long long mul (long long a, long long b, long long mod) {
	a%=mod,b%=mod;
    if (b == 0)
        return 0;
    long long ans = mul (a, b>>1, mod);
    ans = ans*2%mod;
    if (b&1) ans += a, ans %= mod;
    return (ans+mod)%mod;
}*/


int a[maxn],b[maxn],c[3][maxn];
LL Mod=1ll*P[0]*P[1];
LL inv1=pow(P[0],P[1]-2,P[1]),inv2=pow(P[1],P[0]-2,P[0]),inv=pow(Mod%P[2],P[2]-2,P[2]);
inline void solve(int i)
{
	LL C=(mul(1ll*c[0][i]*P[1]%Mod,inv2,Mod)+mul(1ll*c[1][i]*P[0]%Mod,inv1,Mod))%Mod;
	LL K=1ll*((1ll*c[2][i]-(C%P[2]))%P[2])*(inv%P[2])%P[2];
	c[0][i]=(((K%RealMod+RealMod)*(Mod%RealMod)%RealMod+C)%RealMod);
}

int main()
{
	
	//freopen("1.in","r",stdin);
	
	//int t1=clock();
	
	scanf("%d%d",&n,&m);
	for(int i=0;i<=n;i++) scanf("%d",&a[i]);
	for(int j=0;j<=m;j++) scanf("%d",&b[j]);
	for(int i=0;i<3;i++) Prework(i),mul(c[i],a,n,b,m,i);
	for(int i=0;i<=n+m;i++) 
		solve(i); 
	for(int i=0;i<n+m;i++)
	{
		int tmp=(c[0][i]+RealMod)%RealMod;
		printf("%d ",tmp);
	}
	printf("%d\n",(c[0][n+m]+RealMod)%RealMod);
	
	//printf("%d\n",clock()-t1);
}

相關推薦

NTT任意模板+O(1)快速

NTT任意模數的方法其實有點取巧。兩個數列每個有n個數,每個數的大小最多是10^9。如果沒有模數,那麼卷積過後每個位置的答案一定小於10^9*10^9*n,差不多是10^24左右那麼就有一個神奇的做法,選3個乘積大於10^24的NTT模數,分別做一次,得到每個位上模意義下的答

神奇的操作--O(1)快速

col pan urn line nbsp 快速乘 font long div 從同機房大佬那裏聽來的... 用O(1)時間求出兩個相乘超過long long的數的取摸的結果 神奇的操作... inline long long multi(long long x,

O(1)快速

求兩個數相乘超過long long取摸的快速運算O(1)   inline long long multi(long long x,long long y,long long mod) { lon

洛谷 4245 【模板任意NTT

題目:https://www.luogu.org/problemnew/show/P4245 大概是用3個模數分別做一遍,用中國剩餘定理合併。 前兩個合併起來變成一個 long long 的模數,再要和第三個合併的話就爆 long long ,所以可以用一種讓兩個模數的乘積不出現的方法:https://b

洛谷 P4245 [模板]任意NTT —— 三NTT

lld puts || .org font algo std mod char 題目:https://www.luogu.org/problemnew/show/P4245 用三模數NTT做,需要註意時間和細節; 註意各種地方要取模!傳入 upt() 裏面的數一定要不超過2

[洛谷P4245]【模板任意NTT

題目大意:給你兩個多項式$f(x)$和$g(x)$以及一個模數$p(p\leqslant10^9)$,求$f*g\pmod p$ 題解:任意模數$NTT$,最大的數為$p^2\times\max\{n,m\}\leqslant10^{23}$,所以一般選$3$個模數即可,求出這三個模數下的答案,然後中國剩餘

任意NTT

++ include 由於 using ems display ever problem clu 任意模數NTT 眾所周知,為了滿足單位根的性質,\(NTT\)需要質數模數,而且需要能寫成\(a2^{k} + r\)且\(2^k \ge n\) 比較常用的有\(998244

luogu P4245 【模板任意NTT MTT

== tor efi oid n) fine wap 模板 input Code: #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) #define maxn 100

模板】【證明】任意下的二次剩餘求解

什麼是二次剩餘問題 就是求解形如 x 2

[51nod 1258] [伯努利] [多項式求逆] [任意NTT] 序列求和 V4

上次做一套模擬賽的時候,其中需要求自然數k次冪和,然後我只會n^2的…我記得n^2有20分,nlogn求可以爆到90分…… ——鏼鏼鏼2015年國家集訓隊論文 大概就這樣多項式求個逆,求出生成函式就可以了 這樣是O(nlogn),但這道題模數不是滿

淺析樹狀二叉索引樹及一些模板

一個 程序 時間 cst char .org tin define += 樹狀數組   動態連續和查詢問題。給定一個n個元素的數組a1、a2、……,an,設計一個數據結構,支持以下兩種操作:1、add(x,d):讓ax增加d;2、query(l

第21課 可變參模板2_展開參

delet pre 控制 seq src 構造 pro head del 1. 可變參數模板函數 (1)遞歸函數方式展開參數包   ①一般需要提供前向聲明、一個參數包的展開函數和一個遞歸終止函數。   ②前向聲明有時可省略,遞歸終止函數可以是0個或n個參數 (2)逗號表達式

第23課 可變參模板4_Optional和Lazy類的實現

opera -s 緩沖區 data emp 銷毀 由於 方便 lazy load 1. optional類的實現 (1)optional的功能   ①optional<T>的內部存儲空間可能存儲了T類型的值,也可能沒有。只有當optional被T初始化之後,這個

多項式求逆,除法,開方,任意FFT

play 給定 得到 精度 markdown 分配 二次 splay 倒置 多項式求逆 給定\(A(x)\)求滿足\(A(x)*B(x)=1\)的\(B(x)\) 寫成 \[A(x)*B(x)=1(mod \ x^n)\] 我們會求\[A(x)*B(x)=1(mod \ x

Flask:靜態文件&模板0.1

Go chrom 哪裏 emp .com .org 文檔 專業 查看 Windows 10家庭中文版,Python 3.6.4,Flask 1.0.2 前面看了Flask的Quickstart文檔,可是,一直沒有練習裏面的內容,這不,剛剛練習完畢,來寫篇博文記錄一下!

模板四十七

函數模板 泛型編程 代碼復用 多參數定義 函數模板重載 我們到目前為止,學習了 C++ 這麽久。提個小問題:在 C++ 中有幾種交換變量的方法呢?通過定義宏代碼塊和定義函數。下來我們做個實驗#include <iostream> #include <str

嵌入式系統中的轉換ADC

       模擬量是指變數在一定範圍連續變化的量,物理世界的資料幾乎大多數是模擬量,比如溫度,電流,電壓,風速,振動等等。模擬量需要通過取樣,才可能轉化成近似,離散的數字值。    感測器取樣的資料大多數都是模擬量。未來的趨勢是將大幅度增加各種智慧資料採集裝置和感測器,智

轉載1到n這n個整數間的異或值O(1)演算法

轉自http://www.cnblogs.com/flyinghearts/archive/2011/03/22/1992001.html 問題:求1到n這n個整數間的異或值,即 1 xor 2 xor 3 ... xor n 記 f(x, y) 為x到y的所有整