洛谷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;
}
View Code

 

posted @ 2019-08-14 15:25  wangyuchen  阅读(138)  评论(0编辑  收藏  举报