题面

传送门

前置芝士

优化后的\(MTT\)(四次\(FFT\))

题解

这里有多点求值的做法然而被\(shadowice\)巨巨吊起来打了一顿,所以来学一下倍增

成功同时拿到本题最优解和最劣解……

\(Min_{25}\)牛逼!(据说这是原文然而我看不懂就是了)

真的快的不要不要的……

和多点求值一样,我们还是设\(s=\sqrt{n}\),并设多项式

\[g_s(x)=\sum_{i=1}^s(x+i)
\]

求出\(g_s(0),g_s(s),g_s(2s),..,g_s((s-1)s)\)的值,最后再乘上\(\prod_{i=s^2+1}^ni\)即可

具体来说我们现在要完成两个操作

已知

\[g_d(0),g_d(s),...g_d(ds)
\]

\[g_{2d}(0),g_{2d}(s),g_{2d}(2s),...,g_{2d}(2ds)
\]

通过这个操作我们可以把\(d\)乘\(2\)

已知

\[g_d(0),g_d(s),...g_d(ds)
\]

\[g_{d+1}(0),g_{d+1}(s),...g_{d+1}((d+1)s)
\]

通过这个操作我们可以把\(d\)加\(1\)

然后就可以用一个类似快速幂的迭代来求出\(g_s\)了

把\(d\)加\(1\)

这个简单一点先说这个好了

对于\(g_{d+1}((d+1)s)\),直接暴力计算就行了

对于剩下的项,也可以直接暴力计算,因为有

\[g_{d+1}(x)=g_{d}(x)\times (x+d+1)
\]

那么一次的复杂度就是\(O(s)\)的

把\(d\)乘\(2\)

这种情况就比较辣手了

具体来说,我们已知

\[g_d(0),g_d(s),..,g_d(ds)
\]

需要求

\[g_{2d}(0),g_{2d}(s),g_{2d}(2s),...,g_{2d}(2ds)
\]

首先有

\[g_{2d}(x)=g_d(x)g_d(x+d)
\]

我们先考虑一下,已知

\[g_d(0),g_d(s),..,g_d(ds)
\]

如何求出

\[g_d((d+1)s),g_d((d+2)s),..,g_d((d+d)s)
\]

我们可以设\(h(i)=g_d(is)\),那么问题可以转化为已知

\[h(0),h(1),...,h(d)
\]

\[h(d+1),h(d+2),...,h(2d)
\]

另一个问题就是已知

\[g_d(0),g_d(s),..,g_d(2ds)
\]

\[g_{2d}(0),g_{2d}(s),...,g_{2d}(2ds)
\]

也可以转化为已知

\[h(0),h(s),...,h(2d)
\]

\[h(d/s),h(d/s+1),h(d/s+2),...,h(d/s+2d)
\]

总和起来,问题就是已知

\[h(0),h(1),...,h(d)
\]

我们需要求得

\[h(k),k(1+k),...,h(d+k)
\]

根据拉格朗日差值公式,有

\[\begin{aligned}
h(m+k)
&=\sum_{i=0}^n h(i)\prod_{j\neq i}{m+k-j\over i-j}\\
&=\left(\prod_{j=0}^n (m+k-j)\right)\sum_{i=0}^d {h(i)\over i!(d-i)!(-1)^{d-i}}{1\over m+k-i}\\
\end{aligned}
\]

后面明显是个卷积的形式,前面是一段连续区间的乘积,可以用双指针维护

然而万一这里\(m+k-i=0\)怎么办?

因为 \(h(x)\) 本质上代表了一段连续数字的乘积,而根据我们算法的原理, \(h(m+k)\) 和 \(h(i)\) 一定代表了两段不同的数字,所以分母不可能为0

那么我们就可以把\(d\)乘\(2\)了

然而这里有两个细节问题

第一,如果直接求逆元的话是\(O(d\log p)\)的,不过其实有\(O(d)\)求\(n\)个数逆元的方法,感兴趣的可以自己去看这里

第二,虽然\(m\)很小,但是\(m+k\)很大,我们不能直接暴力卷积。注意到后面那个卷积柿子中当\(i>n\)时可以默认\(h(i)=0\),所以有用的项其实只有\(m=0,{1\over m+k-d}\),以及\(m=d,{1\over m+k}\)之间的数,也就是说后面有用的项不超过\(O(d)\)个,所以我们需要把多项式平移一下再来卷积

然后没有然后了

不过可以用威尔逊定理优化一下常数就是了

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
const int N=(1<<17)+5;int P;
inline int add(R int x,R int y){return 0ll+x+y>=P?0ll+x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
R int res=1;
for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
return res;
}
const double Pi=acos(-1.0);
struct cp{
double x,y;
inline cp(){}
inline cp(R double xx,R double yy):x(xx),y(yy){}
inline cp operator +(const cp &b)const{return cp(x+b.x,y+b.y);}
inline cp operator -(const cp &b)const{return cp(x-b.x,y-b.y);}
inline cp operator *(const cp &b)const{return cp(x*b.x-y*b.y,x*b.y+y*b.x);}
inline cp operator *(const double &b)const{return cp(x*b,y*b);}
inline cp operator ~()const{return cp(x,-y);}
}w[2][N];
int r[21][N],ifac[N],lg[N],inv[N];double iv[21];
void Pre(){
iv[0]=1;
fp(d,1,17){
fp(i,0,(1<<d)-1)r[d][i]=(r[d][i>>1]>>1)|((i&1)<<(d-1));
lg[1<<d]=d,iv[d]=iv[d-1]*0.5;
}
inv[0]=inv[1]=ifac[0]=ifac[1]=1;
fp(i,2,131072)inv[i]=mul(P-P/i,inv[P%i]),ifac[i]=mul(ifac[i-1],inv[i]);
for(R int i=1,d=0;i<131072;i<<=1,++d)fp(k,0,i-1)
w[1][i+k]=cp(cos(Pi*k*iv[d]),sin(Pi*k*iv[d])),
w[0][i+k]=cp(cos(Pi*k*iv[d]),-sin(Pi*k*iv[d]));
}
int lim,d;
void FFT(cp *A,int ty){
fp(i,0,lim-1)if(i<r[d][i])swap(A[i],A[r[d][i]]);
cp t;
for(R int mid=1;mid<lim;mid<<=1)
for(R int j=0;j<lim;j+=(mid<<1))
fp(k,0,mid-1)
A[j+k+mid]=A[j+k]-(t=w[ty][mid+k]*A[j+k+mid]),
A[j+k]=A[j+k]+t;
if(!ty)fp(i,0,lim-1)A[i]=A[i]*iv[d];
}
void MTT(int *a,int *b,int len,int *c){
static cp f[N],g[N],p[N],q[N];
lim=len,d=lg[lim];
fp(i,0,len-1)f[i]=cp(a[i]>>16,a[i]&65535),g[i]=cp(b[i]>>16,b[i]&65535);
fp(i,len,lim-1)f[i]=g[i]=cp(0,0);
FFT(f,1),FFT(g,1);
fp(i,0,lim-1){
cp t,f0,f1,g0,g1;
t=~f[i?lim-i:0],f0=(f[i]-t)*cp(0,-0.5),f1=(f[i]+t)*0.5;
t=~g[i?lim-i:0],g0=(g[i]-t)*cp(0,-0.5),g1=(g[i]+t)*0.5;
p[i]=f1*g1,q[i]=f1*g0+f0*g1+f0*g0*cp(0,1);
}
FFT(p,0),FFT(q,0);
fp(i,0,lim-1)c[i]=((((ll)(p[i].x+0.5)%P<<16)%P<<16)+((ll)(q[i].x+0.5)<<16)+((ll)(q[i].y+0.5)))%P;
}
void calc(int *a,int *b,int n,int k){
static int f[N],g[N],h[N],sum[N],isum[N];
int len=1;while(len<=n+n)len<<=1;
fp(i,0,n)f[i]=mul(a[i],mul(ifac[i],ifac[n-i]));
for(R int i=n-1;i>=0;i-=2)f[i]=P-f[i];
int t=dec(k,n);
fp(i,0,n+n)g[i]=add(i,t);
sum[0]=g[0];fp(i,1,n+n)sum[i]=mul(sum[i-1],g[i]);
isum[n+n]=ksm(sum[n+n],P-2);
fd(i,n+n,1)isum[i-1]=mul(isum[i],g[i]);
fp(i,1,n+n)g[i]=mul(isum[i],sum[i-1]);g[0]=isum[0];
fp(i,n+1,len-1)f[i]=0;fp(i,n+n+1,len-1)g[i]=0; MTT(f,g,len,h);
int res=1,p1=k-n,p2=k;
fp(i,p1,p2)res=1ll*res*i%P;
res=dec(res,0); fp(i,0,n)g[i]=(0ll+P+p1+i)%P;
sum[0]=g[0];fp(i,1,n)sum[i]=mul(sum[i-1],g[i]);
isum[n]=ksm(sum[n],P-2);
fd(i,n,1)isum[i-1]=mul(isum[i],g[i]);
fp(i,1,n)g[i]=mul(isum[i],sum[i-1]);g[0]=isum[0]; for(R int i=0;i<=n;p2=add(p2,1),++i)
b[i]=mul(h[i+n],res),res=mul(res,mul(g[i],p2+1));
}
int solve(int bl){
static int a[N],b[N],c[N];
int s=0;for(int p=bl;p;p>>=1)++s;a[0]=1,--s;
int qwq=ksm(bl,P-2);
for(int p=0;s>=0;--s){
if(p){
calc(a,b,p,p+1);
fp(i,0,p)a[p+i+1]=b[i];a[p<<1|1]=0;
calc(a,b,p<<1,mul(p,qwq));
p<<=1;fp(i,0,p)a[i]=mul(a[i],b[i]);
}
if(bl>>s&1){
fp(i,0,p)a[i]=mul(a[i],(1ll*bl*i+p+1)%P);
p|=1,a[p]=1;
fp(i,1,p)a[p]=mul(a[p],(1ll*bl*p+i)%P);
}
}
int res=1;
fp(i,0,bl-1)res=mul(res,a[i]);
return res;
}
int GetFac(int n){
int s=sqrt(n),res=solve(s);
fp(i,s*s+1,n)res=mul(res,i);
return res;
}
int Fac(int n){
if(n>P-1-n){
int res=ksm(GetFac(P-1-n),P-2);
return (P-1-n)&1?res:P-res;
}
return GetFac(n);
}
int n;
int main(){
// freopen("testdata.in","r",stdin);
scanf("%d%d",&n,&P),Pre();
printf("%d\n",Fac(n));
return 0;
}

P5282 【模板】快速阶乘算法(多项式运算+拉格朗日插值+倍增)的更多相关文章

  1. 洛谷P5282 【模板】快速阶乘算法(多项式多点求值+MTT)

    题面 传送门 前置芝士 \(MTT\),多项式多点求值 题解 这题法老当初好像讲过--而且他还说这种题目如果模数已经给定可以直接分段打表艹过去 以下是题解 我们设 \[F(x)=\prod_{i=0} ...

  2. luogu5282 【模板】快速阶乘算法

    由于巨佬 shadowice1984 卡时限,本代码已经 T 请不要粘上去交 退役之后再写一个常数小的多项式取模吧 一句话题意:NP问题,求N!%P 吐槽:出题人太毒瘤...必须写任意模数NTT,而且 ...

  3. BZOJ 2655 calc (组合计数、DP、多项式、拉格朗日插值)

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=2655 题解 据说有一种神仙容斥做法,但我不会. 以及貌似网上大多数人的dp和我的做法都不 ...

  4. 多项式函数插值:全域多项式插值(一)单项式基插值、拉格朗日插值、牛顿插值 [MATLAB]

    全域多项式插值指的是在整个插值区域内形成一个多项式函数作为插值函数.关于多项式插值的基本知识,见“计算基本理论”. 在单项式基插值和牛顿插值形成的表达式中,求该表达式在某一点处的值使用的Horner嵌 ...

  5. BZOJ.4559.[JLOI2016]成绩比较(DP/容斥 拉格朗日插值)

    BZOJ 洛谷 为什么已经9点了...我写了多久... 求方案数,考虑DP... \(f[i][j]\)表示到第\(i\)门课,还有\(j\)人会被碾压的方案数. 那么\[f[i][j]=\sum_{ ...

  6. 【BZOJ】2655: calc 动态规划+拉格朗日插值

    [题意]一个序列$a_1,...,a_n$合法当且仅当它们都是[1,A]中的数字且互不相同,一个序列的价值定义为数字的乘积,求所有序列的价值和.n<=500,A<=10^9,n+1< ...

  7. bzoj3453: tyvj 1858 XLkxc(拉格朗日插值)

    传送门 \(f(n)=\sum_{i=1}^ni^k\),这是自然数幂次和,是一个以\(n\)为自变量的\(k+1\)次多项式 \(g(n)=\sum_{i=1}^nf(i)\),因为这东西差分之后是 ...

  8. 洛谷 P1226 【模板】快速幂||取余运算

    题目链接 https://www.luogu.org/problemnew/show/P1226 题目描述 输入b,p,k的值,求b^p mod k的值.其中b,p,k*k为长整型数. 输入输出格式 ...

  9. LG4781 【模板】拉格朗日插值

    题意 题目描述 由小学知识可知,$n$个点$(x_i,y_i)$可以唯一地确定一个多项式 现在,给定$n$个点,请你确定这个多项式,并将$k$代入求值 求出的值对$998244353$取模 输入输出格 ...

随机推荐

  1. 问题记录,StartCoroutine(“str")问题

    StartCoroutine参数为函数字符串名,运行时出错,错误是:无法启动协程函数. 调用格式如下: gameManager.StartCoroutine(LuaOnLevelwasloaded() ...

  2. javascript数组操作(创建、元素删除、数组的拷贝)

    这篇文章主要介绍了javascript数组操作,包括创建.元素的访问.元素删除.数组的拷贝等操作,还有其它示例,需要的朋友可以参考下 1.数组的创建 复制代码 代码如下: var arrayObj = ...

  3. 多产生半成品工单的问题 修改带SO的半成品工单无法分配给SO的问题的

    原因:验货及VIP带SO的半成品MO无法分配给对应的SO问题, 解决: SELECT SL.ENTERPRISE , SL.ENGINE_ID , SL.SITEID , ML.MO_ID || '_ ...

  4. Git报错的解决方案汇总

    错误1: error: Your local changes to the following files would be overwritten by merge:Please, commit y ...

  5. js获取不到动态添加的标签的值的解决方法

    遇到了js无法获得动态添加的标签的值,百度了一番,最后自己解决了问题,但是原理现在还不怎么明确. $("input[id='txtAttValue']").each(functio ...

  6. jQuery validator 增加多个模板

    今天学了jquery validator 可以增加多个模板,而不用写长长的js代码.废话少说,直接上例子 这段是要添加的模板 上面是把模板部分是要重复增加多个的部分,需独立出来,用textarea标签 ...

  7. nDPI深度数据包检测

    nDPI https://github.com/ntop/nDPI

  8. Text Relatives II

    [Text Relatives II] When your app determines that the user has requested the edit menu—which could b ...

  9. java集成支付宝移动快捷支付时报错java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence

    出错原因是代码中的私钥设置错误,不是填原始的私钥,而是转换为PKCS8格式的私钥(Java格式的) ,改成后就会报创建交易异常了

  10. Linux服务器上日志报com.mysql.jdbc.PacketTooBigException: Packet for query is too large (1783 > 1024). You can change this value on the server by setting the max_allowed_packet' variable.

    在做查询数据库操作时,报了以上错误,还有out of memery heap hacp ,原因是MySQL的max_allowed_packet设置过小引起的,我一开始设置的是1M,后来改为了20M ...