洛谷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