题目大意
墙上有 n n n个红色的钉子,有一根橡皮筋围在钉子上,形成了⼀个正边形。在正边形的中⼼处有⼀个蓝⾊的钉⼦,蓝⾊钉⼦的直径⽐红⾊钉⼦⼩。现在你拔掉若⼲钉⼦,当橡⽪筋最终碰到了蓝⾊钉⼦,游戏结束。问你有多少种有序的拔掉红⾊钉⼦的⽅案。答案模⼀个质数 p p p。
3 ≤ n ≤ 5000 3\leq n\leq 5000 3≤n≤5000, 1 0 8 ≤ p ≤ 1 0 9 10^8\leq p\leq 10^9 108≤p≤109
题解
设 t = ⌊ n 2 ⌋ t=\lfloor\dfrac n2\rfloor t=⌊2n⌋,则去掉连续的 i ( i ≥ t ) i(i\geq t) i(i≥t)个红钉子之后,游戏结束。因为是正 n n n边形,所以从哪个点开始取走一段都可以,不妨设从第一个点开始取走一段,最后再乘上 n n n。
我们可以枚举 i i i,在这 i i i个点中,总有一个点要在最后去掉。它与最后一个点之间的点的个数不能超过 t t t,所以前 i − t i-t i−t个点不能选;它与第一个点之间的点的个数不能超过 t t t,所以后 i − t i-t i−t个点不能选,所以它能选择的点为 i − 2 ( i − t ) = t − i i-2(i-t)=t-i i−2(i−t)=t−i个点。
剩下能选的点有 n − 2 − i n-2-i n−2−i个。枚举选的点 j ( j ≤ n − 2 − i ) j(j\leq n-2-i) j(j≤n−2−i),则这些点有 C n − 2 − i j C_{n-2-i}^j Cn−2−ij种选择。
最后一个点必须最后取,选其余点的顺序任意,所以还要乘上 ( j + i − 1 ) ! (j+i-1)! (j+i−1)!。
方案数为
∑ i = t n − 2 ( 2 ∗ t − i ) × n × ∑ j = 0 n − 2 − i C n − 2 − i j × ( j + i − 1 ) ! \sum\limits_{i=t}^{n-2}(2*t-i)\times n\times \sum\limits_{j=0}^{n-2-i}C_{n-2-i}^j\times (j+i-1)! i=t∑n−2(2∗t−i)×n×j=0∑n−2−iCn−2−ij×(j+i−1)!。
一些特判:
当
i
=
n
−
1
i=n-1
i=n−1时
- 如果 n n n为奇数,则因为如果只剩两个点,游戏一定结束,所以不可能能够删到只剩一个点,此时贡献为 0 0 0
- 如果 n n n为偶数,因为 n − i − 2 = − 1 n-i-2=-1 n−i−2=−1,出现了负数,所以要特判。这种情况下的贡献为 ( i − 1 ) ! (i-1)! (i−1)!
时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
code
#include<bits/stdc++.h>
using namespace std;
int n;
long long p,ans,jc[5005],ny[5005];
long long mi(long long t,long long v){
if(!v) return 1;
long long re=mi(t,v/2);
re=re*re%p;
if(v&1) re=re*t%p;
return re;
}
long long C(int x,int y){
return jc[x]*ny[y]%p*ny[x-y]%p;
}
int main()
{
scanf("%d%lld",&n,&p);
jc[0]=1;
for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%p;
ny[n]=mi(jc[n],p-2);
for(int i=n-1;i>=0;i--) ny[i]=ny[i+1]*(i+1)%p;
for(int i=n/2;i<n;i++){
if((n&1)&&i==n-1) break;
long long tmp=0;
int v=n-i-2;
if(i==n-1) tmp=jc[i-1];
else{
for(int j=0;j<=v;j++){
tmp=(tmp+C(v,j)*jc[j+i-1]%p)%p;
}
}
ans=(ans+((n/2)*2-i)*tmp%p)%p;
}
ans=n*ans%p;
printf("%lld",ans);
return 0;
}