洛谷P5509 派遣
题意:已知一些数对$(x,y)(0\le x<n,0\le y<k,x+y>0)$,可选可不选,每种选法对答案的贡献是所有$\frac{x}{(k-1)x+y}$之积(一个都没选则为1),求总贡献。
首先提取公因式就可以看出答案是所有$(x,y)$的$(1+\frac{x}{(k-1)x+y})$之积,即$\prod\limits_{0\le x<n,0\le y<k,x+y>0}\frac{kx+y}{(k-1)x+y}$,这样就可以$O(Tnk)$求了。
但是这个东西好像可以化简,我们来试一下:
$x=0$时,$\prod\limits^{k-1}_{y=1}\frac{y}{y}=1$;
$x=1$时,$\prod\limits^{k-1}_{y=0}\frac{k+y}{k-1+y}=\frac{2k-1}{k-1}$;
$x=2$时,$\prod\limits^{k-1}_{y=0}\frac{2k+y}{2k-2+y}=\frac{(3k-1)(3k-2)}{(2k-1)(2k-2)}$;
发现规律:对于固定的$x$,积为$\frac{\prod\limits^x_{i=1}(k(x+1)-i)}{\prod\limits^x_{j=1}(kx-j)}$,把这些东西乘起来,许多项上下消掉了!确切地说,只剩下$\frac{\prod\limits^{n-1}_{i=1}(kn-i)}{\prod\limits^{n-1}_{j=1}(kj-j)}$(可以自己试一下),化简得到:$\frac{\prod\limits^{kn-1}_{i=kn-n+1}i}{(n-1)!(k-1)^{n-1}}$,这样至少可以$O(Tn)$求。
但是,都推到这一步了,我们肯定不甘心暴力求,而求这个式子的效率瓶颈在于快速求$n!(n\le 10^{18})$,所以我们需要一个套路的公式:
$$n!\equiv(n\;mod\;p)!\times (p!)^{n/p}\times (n/p)!(mod\;p)$$
注意到题目中得$p$比较小,预处理一下阶乘和逆元就可以了。
另外一个要注意的问题是$n>p$时,直接取模会得到$0$,不过由于每个数肯定可以写成$a\times p^m(gcd(a,p)=1,m\ge 0)$的形式,我们把$a$取模,维护$m$,若分母的$m$大于分子,则为-1;若小于分子,则为0;否则就是两个$a$做有理数取余。
#include<cstdio> typedef long long ll; char rB[1<<23],*rS,*rT; inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<23,stdin),rS==rT)?EOF:*rS++;} inline int rd(){ char c=gc(); while(c<48||c>57)c=gc(); int x=c&15; for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15); return x; } const int mod=1145141; int f[mod],g[mod],inv[mod]; inline int fp(int a,ll p){ int s=1; while(p){ if(p&1ll)s=(ll)s*a%mod; a=(ll)a*a%mod; p>>=1ll; } return s; } struct data{ int v; ll p; data(){} data(int v,ll p):v(v),p(p){} inline data operator *(const data &b)const{return data((ll)v*b.v%mod,p+b.p);} //乘法:系数相乘,指数相加 inline data operator /(const data &b)const{return data((ll)v*inv[b.v]%mod,p-b.p);} //除法相反 }; inline data getf(ll x){ return x<mod?data(f[x],0ll):data((ll)fp(f[mod-1],x/mod)*f[x%mod]%mod,x/mod)*getf(x/mod); } inline data getmul(ll x,ll y){ //求(x+1)*(x+2)*...*y mod p data t1=getf(y),t2=getf(x); return t1/t2; } int main(){ int T=rd(),n,k,i; data t1,t2,t3; for(i=2,inv[1]=f[0]=f[1]=1;i<mod;++i)f[i]=(ll)f[i-1]*i%mod; for(i=2;i<mod;++i)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod; while(T--){ n=rd();k=rd(); t1=getmul((ll)k*n-n,(ll)k*n-1ll); //(kn-n+1)*(kn-n+2)*...*(kn-1) t2=getf(n-1); //(n-1)! t3=(k-1)%mod?data(fp((k-1)%mod,n-1),0ll):data(fp((k-1)/mod,n-1),n-1); //(k-1)^(n-1) if(t1.p<t2.p+t3.p)puts("-1"); else if(t1.p>t2.p+t3.p)puts("0"); else printf("%d\n",(ll)t1.v*inv[t2.v]%mod*inv[t3.v]%mod); } return 0; }