[题解] Luogu P4245 [模板]任意模数NTT

三模NTT 不会。。。

都0202年了,还有人写三模NTT啊。。。



讲一个好写点的做法吧:

首先取一个阀值\(w\),然后把多项式的每个系数写成\(aw + c(c < w)\)的形式,换句话说把多项式\(f(x)\)写成两个多项式相加的形式:
\[
f(x) = wf_0(x) + f_1(x)
\]
这样在这道题中取\(W = 2^{15}\)就可以避免爆long long了。

乘起来的话就是
\[
f \cdot g = (w f_0 + f_1)(wg_0 + g_1) = (f_0 g_0)w^2 + (f_0g_1 + f_1g_0) w + f_1g_1
\]
这样我们只要算\(f_0g_0, (f_0g_1 + f_1g_0), f_1g_1\)就好了,分别\(FFT\)算一下。(这玩意儿好像叫\(MTT\),妙~啊)

这样要做\(7\)次\(FFT\),好像可以做到\(4\)次,不会。

感觉这样也跑的挺快的。

\(Code\)

#include <bits/stdc++.h>
using namespace std;
typedef long double db;
typedef long long ll;
const db PI=acos(-1.0);
const int N=3e5+10;
struct cpl{
    db x,y;
    cpl operator + (cpl k1)const{return (cpl){x+k1.x,y+k1.y};}
    cpl operator - (cpl k1)const{return (cpl){x-k1.x,y-k1.y};}
    cpl operator * (cpl k1)const{return (cpl){x*k1.x-y*k1.y,x*k1.y+y*k1.x};}
};
int rev[N];
void fft(cpl *f,int n,int k1){
    for (int i=0;i<n;i++)
        if (rev[i]<i)swap(f[i],f[rev[i]]);
    for (int len=2;len<=n;len<<=1){
        cpl wn=(cpl){cos(2*PI/len),k1*sin(2*PI/len)};
        for (int i=0;i<n;i+=len){
            cpl w=(cpl){1,0};
            for (int j=i;j<i+(len>>1);j++){
                cpl tmp=w*f[j+(len>>1)];
                f[j+(len>>1)]=f[j]-tmp;
                f[j]=f[j]+tmp;
                w=w*wn;
            }
        }
    }
}
cpl f[2][N],g[2][N],ans[3][N];
#define normal(x) (((ll)(x/limit+0.5)%mod+mod)%mod)
void mtt(int *a,int n,int *b,int m,int mod){
    int limit=1; while (limit<=n+m)limit<<=1;
    for (int i=0;i<limit;i++) rev[i]=rev[i>>1]>>1|((i&1)?limit>>1:0);
    for (int i=0;i<limit;i++){
        f[0][i].x=a[i]>>15;f[1][i].x=a[i]&0x7fff;
        g[0][i].x=b[i]>>15;g[1][i].x=b[i]&0x7fff;
    }
    fft(f[0],limit,1),fft(f[1],limit,1);
    fft(g[0],limit,1),fft(g[1],limit,1);
    for (int i=0;i<limit;i++){
        ans[0][i]=f[0][i]*g[0][i];
        ans[1][i]=f[0][i]*g[1][i]+f[1][i]*g[0][i];
        ans[2][i]=f[1][i]*g[1][i];
    }
    fft(ans[0],limit,-1),fft(ans[1],limit,-1),fft(ans[2],limit,-1);
    for (int i=0;i<=n+m;i++){
        ll k1=(normal(ans[0][i].x)<<30ll)%mod;
        ll k2=(normal(ans[1][i].x)<<15ll)%mod;
        ll k3=normal(ans[2][i].x)%mod;
        printf("%d ",((k1+k2)%mod+k3)%mod);
    }
}
int n,m,a[N],b[N],mod;
int main(){
    scanf("%d%d%d",&n,&m,&mod);
    for (int i=0;i<=n;i++)scanf("%d",&a[i]);
    for (int i=0;i<=m;i++)scanf("%d",&b[i]);
    mtt(a,n,b,m,mod);
    return 0;
}

原文地址:https://www.cnblogs.com/wxq1229/p/12235793.html

时间: 2024-10-09 07:20:14

[题解] Luogu P4245 [模板]任意模数NTT的相关文章

任意模数NTT(MTT)模板

记住代码里3个模数,它们的原根都是3.考虑通过3个模数下的答案用中国剩余定理乱搞,得出答案.常数较大.有个什么拆系数FFT不会. P4245 [模板]任意模数NTT #include<bits/stdc++.h> using namespace std; typedef long long LL; #define rint register int const int mod[3]={469762049,998244353,1004535809}; const int N=400010; in

MTT:任意模数NTT

MTT:任意模数NTT 概述 有时我们用FFT处理的数据很大,而模数可以分解为\(a\cdot 2^k+1\)的形式.次数用FFT精度不够,用NTT又找不到足够大的模数,于是MTT就应运而生了. MTT没有模数的限制,比NTT更加自由,应用广泛,可以用于任意模数或很大的数. MTT MTT是基于NTT的,其思想很简单,就是做多次NTT,每次使用不同的素数,然后使用CRT合并解,在合并的过程中模最终模数,或是对于无模数的情况使用高精度. 做NTT的次数取决于最大可能答案的大小,所用的所有素数之积必

洛谷P4245 【模板】MTT(任意模数NTT)

题目背景 模板题,无背景 题目描述 给定 22 个多项式 F(x), G(x)F(x),G(x) ,请求出 F(x) * G(x)F(x)∗G(x) . 系数对 pp 取模,且不保证 pp 可以分解成 p = a \cdot 2^k + 1p=a⋅2k+1 之形式. 输入输出格式 输入格式: 输入共 33 行.第一行 33 个整数 n, m, pn,m,p ,分别表示 F(x), G(x)F(x),G(x) 的次数以及模数 pp .第二行为 n+1n+1 个整数, 第 ii 个整数 a_iai?

luogu P4245 【模板】任意模数NTT MTT

Code: #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) #define maxn 1000000 #define M 32768 #define double long double #define ll long long using namespace std; namespace poly{ const double pi=acos(-1); int rev[

[luogu P3384] [模板]树链剖分

[luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数

【51nod】1123 X^A Mod B (任意模数的K次剩余)

题解 K次剩余终极版!orz 写一下,WA一年,bug不花一分钱 在很久以前,我还认为,数论是一个重在思维,代码很短的东西 后来...我学了BSGS,学了EXBSGS,学了模质数的K次剩余--代码一个比一个长-- 直到今天,我写了240行的数论代码,我才发现数论这个东西= =太可怕了 好吧那么我们来说一下任意模数的K次剩余怎么搞 首先,如果模数是奇数,我们可以拆成很多个质数的指数幂,再用解同余方程的方法一个个合起来,细节之后探讨 但是如果,模数有偶数呢 因为要输出所有解,解的个数不多,我们可以倍

题解 luogu P1850 【换教室】

题解 luogu P1850 [换教室] 时间:2019.8.6 一晚上(约 3.5h 写完) 题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有 \(2n\) 节课程安排在 \(n\) 个时间段上.在第 \(i\)(\(1 \leq i \leq n\))个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 \(c_i\) 上课,而另一节课程在教室 \(d_i\) 进行. 在不提交任何申请的情况下,学生们需要

luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)

luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #inc

题解 luogu P5021 【赛道修建】

题解 luogu P5021 [赛道修建] 时间:2019.8.9 20:40 时间:2019.8.12 题目描述 C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 \(m\) 条赛道. C 城一共有 \(n\) 个路口,这些路口编号为 \(1,2,\dots,n\),有 \(n-1\) 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口.其中,第 \(i\) 条道路连接的两个路口编号为 \(a_i\) 和 \(b_i\),该道路的长度为 \(l_i\).借助这 \(n-1\) 条