首先,种树不外乎三种情况:两头有树、一头有数、两头都没树。这三种情况互不影响,这里只讨论两头有树的情况。
不考虑树的顺序,则根据插板法把问题化归为如下情况:把$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;
}
