LGP5282题解

Min_25牛逼!Min_25牛逼!Min_25牛逼!

设有 \(f_d(x)=\prod_{i=1}^{d}(x+i)=f_{d-1}(x)(x+d)\),设 \(B=\lfloor\sqrt{n}\rfloor\),那么计算阶乘就相当于需要计算 \(f_B(iB)\)\(i\in[0,B]\)

考虑倍增。如果我们知道了 \(f_d(iB)\)\(i\in[0,d]\)),考虑应该如何计算 \(f_{d+1}(iB))\)\(i\in[0,d+1]\)) 和 \(f_{2d}(iB)\)\(i\in[0,2d]\)

第一个是较为显然的,只需要对 \(x+d+1\) 求出 \(x\in[0,dB],B\mid x\) 时的点值即可,对于 \(f_{d+1}(dB)\) 直接暴力计算即可。

第二个比较麻烦。

根据定义显然有 \(f_{2d}(x)=f_d(x)f_d(x+d)\)。也就是说我们只要知道了 \(f_d(iB),f_d(iB+d)\)\(i\in[0,2d]\)) 的点值即可。

\(g_d(x)=f_d(xB)\),上述问题变为给定 \(g_d(x)\)\(x\in[0,2d]\)),求 \(g(x+\frac{d}{B})\)\(x\in[0,2d]\))。

很显然在模意义下有 \(\frac{d}{B}>B\)

证明:因为 \(n<p\),所以对于 \(k\in[0,B]\) 都有 \(kB<n<p\),所以 \(kB\ne d\),那么 \([0,B]\) 中一定不存在 \(\frac{d}{B}\)

你会发现这个和 拉格朗日插值2 很像,但是我们并不知道 \(g_d(i)\)\(i\in[d,2d]\))。

可以考虑通过 \(g_d(i)\)\(i\in[0,d]\))求出 \(g_d(i)\)\(i\in[d,2d]\)),再求上面的那个东西。

这里可以直接套用 LOJ166 的做法了。复杂度 \(T(n)=T(\frac{n}{2})+O(\log n)=T(n\log n)\),这里 \(n=\sqrt N\) 所以复杂度是 \(O(\sqrt{n}\log n)\)

第二部分的流程:\(f_d(iB)\)\(i\in[0,d]\))->\(f_d(iB)\)\(i\in[0,2d]\))->\(f_d(iB+d)\)\(i\in[2d]\))->\(f_{2d}(iB)\)\(i\in[0,2d]\)

#include<cassert>
#include<cstdio>
#include<cmath>
#define IMP(lim,act) for(int qwq=(lim),i=0;i^qwq;++i)act
typedef double db;
const int M=3e5+5;
const db Pi=acos(-1);
int n,P,F[M],ifac[M];
struct Barrett{
	typedef unsigned long long ull;
	typedef __uint128_t LL;
	ull m,B;
	Barrett(const ull&m=2):m(m),B((LL(1)<<64)/m){}
	friend inline ull operator%(const ull&a,const Barrett&mod){
		ull r=a-mod.m*(LL(mod.B)*a>>64);return r>=mod.m?r-mod.m:r;
	}
}mod;
struct complex{
	db x,y;
	complex(const db&x=0,const db&y=0):x(x),y(y){}
	inline complex operator+(const complex&it)const{
		return complex(x+it.x,y+it.y);
	}
	inline complex operator-(const complex&it)const{
		return complex(x-it.x,y-it.y);
	}
	inline complex operator*(const complex&it)const{
		return complex(x*it.x-y*it.y,x*it.y+y*it.x);
	}
}buf[M],*w[20];
inline int Getlen(const int&n){
	int len(0);while((1<<len)<n)++len;return len;
}
inline void swap(complex&a,complex&b){
	complex c=a;a=b;b=c;
}
inline int Get(const db&x){
	return((long long)(x+.5))%mod;
}
inline int qpow(int a,int b=P-2){
	int ans(1);for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;return ans;
}
inline void NTT_init(const int&n){
	const int&m=Getlen(n)-1;complex*now=buf;w[m]=now;now+=1<<m;
	IMP(1<<m,w[m][i]=complex(std::cos(i*Pi/(1<<m)),std::sin(i*Pi/(1<<m))));
	for(int k=m-1;k>=0&&(w[k]=now,now+=1<<k);--k)IMP(1<<k,w[k][i]=w[k+1][i<<1]);
}
inline void DFT(complex*f,const int&M){
	const int&n=1<<M;
	for(int len=n>>1,d=M-1;d>=0;--d,len>>=1)for(int k=0;k^n;k+=len<<1){
		complex*W=w[d],*L=f+(k),*R=f+(k|len),x,y;IMP(len,(x=*L,y=*R)),*L++=(x+y),*R++=*W++*(x-y);
	}
}
inline void IDFT(complex*f,const int&M){
	const int&n=1<<M;
	for(int len=1,d=0;d^M;++d,len<<=1)for(int k=0;k^n;k+=len<<1){
		complex*W=w[d],*L=f+(k),*R=f+(k|len),x,y;IMP(len,(x=*L,y=*W++**R)),*L++=(x+y),*R++=(x-y);
	}
	IMP(n,(f[i].x/=n,f[i].y/=n));for(int i=1;(i<<1)<n;++i)swap(f[i],f[n-i]);
}
inline void MTT(int*f,int*g,int*h,const int&n,const int&m,const int&t,const int&Len=-1){
	static complex Q[M],P[M],T[M];const int&len=Getlen(!~Len?n+m-1:Len);
	IMP(n,(Q[i].x=f[i]&32767,P[i].x=f[i]>>15));IMP(m,T[i]=complex(g[i]&32767,g[i]>>15));
	DFT(Q,len);DFT(P,len);DFT(T,len);IMP(1<<len,(Q[i]=Q[i]*T[i],P[i]=P[i]*T[i]));IDFT(Q,len);IDFT(P,len);
	IMP(t,h[i]=(Get(Q[i].x)+(1ll*Get(Q[i].y+P[i].x)<<15)+(1ll*Get(P[i].y)<<30))%mod);IMP(1<<len,Q[i]=P[i]=T[i]=0);
}
inline void Getinv(int*f,const int&n){
	static int pre[M];pre[0]=f[0];for(int i=1;i<n;++i)pre[i]=1ll*pre[i-1]*f[i]%mod;
	int t,c=qpow(pre[n-1]);for(int i=n-1;i>=1;--i)t=f[i],f[i]=1ll*pre[i-1]*c%mod,c=1ll*c*t%mod;f[0]=c;
}
inline void PT(int*f,int*g,const int&n,const int&m){
	static int F[M],G[M],H[M];H[0]=1;IMP(n,H[0]=1ll*H[0]*(m-i)%mod);IMP(n+n,G[i]=m+i-n);G[0]=1;Getinv(G,n+n);
	IMP(n,F[i]=1ll*ifac[i]*(n-i-1&1?P-ifac[n-i-1]:ifac[n-i-1])%mod*f[i]%mod);
	const int&len=Getlen(n+n);for(int i=1;i<n;++i)H[i]=1ll*(m+i)*G[i]%mod*H[i-1]%mod;MTT(F,G,F,n,n+n,n+n,n+n);
	IMP(n,g[i]=1ll*F[n+i]*H[i]%mod);IMP(1<<len,F[i]=G[i]=H[i]=0);
}
inline int Getfac(const int&n,const int&p){
	static int F[M],G[M];const int&B=sqrt(n),m=Getlen(B-1)-1;
	mod=Barrett(P=p);ifac[0]=ifac[1]=1;for(int i=2;i<=B;++i)ifac[i]=1ll*(P-P/i)*ifac[P%i]%mod;
	for(int i=1;i<=B;++i)ifac[i]=1ll*ifac[i-1]*ifac[i]%mod;F[0]=1;F[1]=B+1;
	for(int i=m;i>=0;--i){
		const int&q=B>>i+1,p=B>>i;if(!q)continue;
		PT(F,G,q+1,q+1);IMP(q,F[i+q+1]=G[i]),G[i]=0;G[q]=0;
		PT(F,G,q*2+1,1ll*q*qpow(B)%mod);IMP(q*2+1,F[i]=1ll*F[i]*G[i]%mod),G[i]=0;
		if(q*2+1==p){
			IMP(q*2+1,F[i]=1ll*F[i]*(i*B+p)%mod);F[p]=1;IMP(p,F[p]=1ll*F[p]*(p*B+i+1)%mod);
		}
	}
	int ans(1);IMP(B,ans=1ll*ans*F[i]%mod),F[i]=0;F[B]=0;for(int i=B*B+1;i<=n;++i)ans=1ll*ans*i%mod;return ans;
}
signed main(){
	int T,N,P;NTT_init(1<<17);scanf("%d",&T);while(T--)scanf("%d%d",&N,&P),printf("%d\n",Getfac(N,P));
}
posted @ 2022-06-30 10:38  Prean  阅读(14)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};