CF1764D Doremy‘s Pegging Game

该文章介绍了一个编程竞赛问题,涉及一个正多边形和中心的蓝色钉子。玩家需要拔掉红色钉子,使橡皮筋接触到蓝色钉子。解决方案包括利用组合数学计算不同拔钉子策略的数量,并优化时间复杂度至O(n^2)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CF1764D Doremy’s Pegging Game

题目大意

墙上有 n n n个红色的钉子,有一根橡皮筋围在钉子上,形成了⼀个正边形。在正边形的中⼼处有⼀个蓝⾊的钉⼦,蓝⾊钉⼦的直径⽐红⾊钉⼦⼩。现在你拔掉若⼲钉⼦,当橡⽪筋最终碰到了蓝⾊钉⼦,游戏结束。问你有多少种有序的拔掉红⾊钉⼦的⽅案。答案模⼀个质数 p p p

3 ≤ n ≤ 5000 3\leq n\leq 5000 3n5000 1 0 8 ≤ p ≤ 1 0 9 10^8\leq p\leq 10^9 108p109


题解

t = ⌊ n 2 ⌋ t=\lfloor\dfrac n2\rfloor t=2n,则去掉连续的 i ( i ≥ t ) i(i\geq t) i(it)个红钉子之后,游戏结束。因为是正 n n n边形,所以从哪个点开始取走一段都可以,不妨设从第一个点开始取走一段,最后再乘上 n n n

我们可以枚举 i i i,在这 i i i个点中,总有一个点要在最后去掉。它与最后一个点之间的点的个数不能超过 t t t,所以前 i − t i-t it个点不能选;它与第一个点之间的点的个数不能超过 t t t,所以后 i − t i-t it个点不能选,所以它能选择的点为 i − 2 ( i − t ) = t − i i-2(i-t)=t-i i2(it)=ti个点。

剩下能选的点有 n − 2 − i n-2-i n2i个。枚举选的点 j ( j ≤ n − 2 − i ) j(j\leq n-2-i) j(jn2i),则这些点有 C n − 2 − i j C_{n-2-i}^j Cn2ij种选择。

最后一个点必须最后取,选其余点的顺序任意,所以还要乘上 ( j + i − 1 ) ! (j+i-1)! (j+i1)!

方案数为

∑ 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=tn2(2ti)×n×j=0n2iCn2ij×(j+i1)!

一些特判:
i = n − 1 i=n-1 i=n1

  • 如果 n n n为奇数,则因为如果只剩两个点,游戏一定结束,所以不可能能够删到只剩一个点,此时贡献为 0 0 0
  • 如果 n n n为偶数,因为 n − i − 2 = − 1 n-i-2=-1 ni2=1,出现了负数,所以要特判。这种情况下的贡献为 ( i − 1 ) ! (i-1)! (i1)!

时间复杂度为 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值