其他分享
首页 > 其他分享> > 洛谷P5520 青原樱(组合数学)

洛谷P5520 青原樱(组合数学)

作者:互联网

首先,种树不外乎三种情况:两头有树、一头有数、两头都没树。这三种情况互不影响,这里只讨论两头有树的情况。

不考虑树的顺序,则根据插板法把问题化归为如下情况:把$n-m$个空位安排到$m-1$个间隔里,要求每个间隔非空。即把$n-m$个元素划分成$m-1$个非空段的方案数。再用一次插板法可知答案为$C^{m-2}_{n-m-1}$。

同理可得另外两种情况的答案。合并得到最终答案:$(C^{m-2}_{n-m-1}+2C^{m-1}_{n-m-1}+C^m_{n-m-1})\times m!$(一头有树有左右两种可能)

由于没想到可以约分就直接用质因数分解硬做了

#include<cstdio>
#include<cstring>
typedef long long ll;
int p[50],pcnt=0,mod;
void exgcd(int a,int b ,int &x,int &y){
    if(!b){x=1;y=0;}
    else{
        exgcd(b,a%b,y,x);
        y-=a/b*x;
    }
}
inline int inv(int a){
    int x,y;
    exgcd(a,mod,x,y);
    return x<0?x+mod:x;
}
inline int fp(int a,int p){
    int s=1;
    while(p){
        if(p&1)s=(ll)s*a%mod;
        a=(ll)a*a%mod;
        p>>=1;
    }
    return s;
}
struct node{
    int a,k[50];
    inline void operator *=(int x){
        for(int i=1;i<=pcnt;++i)
            while(!(x%p[i])){++k[i];x/=p[i];}
        a=(ll)a*x%mod;
    }
    inline void operator /=(int x){
        for(int i=1;i<=pcnt;++i)
            while(!(x%p[i])){--k[i];x/=p[i];}
        a=(ll)a*inv(x)%mod;
    }
}s;
inline void work(int mod){
    for(int i=2;i*i<=mod;++i)if(!(mod%i)){
        p[++pcnt]=i;
        while(!(mod%i))mod/=i;
    }
    if(mod>1)p[++pcnt]=mod;
}
inline int C(int n,int m){
    if(n<m)return 0;
    int i,ans=1;
    s.a=1;memset(s.k,0,sizeof(s.k));
    for(i=0;i<m;++i)s*=n-i;
    for(i=2;i<=m;++i)s/=i;
    ans=s.a;
    for(i=1;i<=pcnt;++i)ans=(ll)ans*fp(p[i],s.k[i])%mod;
    return ans;
}
int main(){
    int n,m,i,ans;
    scanf("%d%d%d%d",&i,&n,&m,&mod);
    if(mod==1){
        puts("0");
        return 0;
    }
    if(m==1){
        printf("%d\n",n%mod);
        return 0;
    }
    if(n==1){
        puts("1");
        return 0;
    }
    work(mod);
    ans=((ll)C(n-m-1,m-2)+(C(n-m-1,m-1)<<1)+C(n-m-1,m))%mod;
    for(i=2;i<=m;++i)ans=(ll)ans*i%mod;
    printf("%d",ans);
    return 0;
}
View Code

 

标签:P5520,插板,洛谷,int,青原,exgcd,inline,两头,mod
来源: https://www.cnblogs.com/sunshine-chen/p/11412562.html