P5520 [yLOI2019] 青原樱
原题链接
考察:组合数学
直接给我梦回高中排列组合,考的是插空法,我完全没印象....
思路:
先排列m个树苗,方法是\(m!\),接下来先将空插到树苗中间,此时还剩下\(n-2*m+1\)个空位,这\(n-2*m+1\)个空位分为m+1组,这里可以用隔板法求解,求组合数我求麻烦了点,可以直接求.
Code
#include <iostream>
#include <cstring>
using namespace std;
const int N = 2000010;
typedef long long LL;
int type,n,m,p,prime[N],cnt;
bool st[N];
void GetPrime(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) prime[++cnt] = i;
for(int j=1;prime[j]<=n/i;j++)
{
st[i*prime[j]] = 1;
if(i%prime[j]==0) break;
}
}
}
int qsm(int a,int k,int p)
{
int res = 1;
while(k)
{
if(k&1) res = (LL)res*a%p;
a = (LL)a*a%p;
k>>=1;
}
return res;
}
int A(int a,int b)
{
int res = 1;
for(int i=a;i>a-b;i--) res = (LL)res*i%p;
return res;
}
int get(int a,int p)
{
int ans = 0;
while(a)
{
ans+=a/p;
a/=p;
}
return ans;
}
int C(int a,int b)
{
if(a<b) return 0;
int ans = 1;
for(int i=1;i<=cnt;i++)
{
int t = prime[i];
int s = get(a,t)-get(b,t)-get(a-b,t);
ans = (LL)ans*qsm(t,s,p)%p;
}
return ans;
}
int main()
{
scanf("%d%d%d%d",&type,&n,&m,&p);
GetPrime(n);
if(n-2*m+1<0) {puts("0");return 0;}
int ans = (LL)A(m,m)*C(n-m+1,m)%p;
printf("%d\n",ans);
return 0;
}