2023牛客暑期多校训练营3 B- Auspiciousness(dp)

B- Auspiciousness

目录

题目

题目大意

思路

坑点


题目

Dog Card is a card game. In the game, there are a total of 2n2n cards in the deck, each card has a value, and the values of these 2n2n cards form a permutation of 1\sim 2n1∼2n. There is a skill that works as follows:

  1. Draw a card from the top of the deck.
  2. If the deck is empty, then skip to step 33, otherwise you guess whether the card on the top of the deck has a higher value than your last drawn card and draw a card from the top of the deck. If your guess is correct, then repeat this step, otherwise skip to step 3.
  3. End this process.

Nana enjoys playing this game, although she may not be skilled at it. Therefore, her guessing strategy when using this skill is simple: if the value of the last drawn card is less than or equal to nn, then she guesses that the next card's value is higher; otherwise, she guesses that the next card's value is lower. She wants to know, for all different decks of cards (Obviously, there are (2n)!(2n)! cases), how many cards she can draw in total if she uses the skill only once in each case. Since this number can be very large, please provide the answer modulo a given value.

题目大意

依照【上一张牌不超过n则猜测牌堆顶大于上一张牌,否则猜测小于】的策略猜测

问对于所有可能的(2n)!种牌堆叠放顺序,总共能取走的牌的数量之和模m的结果

思路

 1~n为小数,n+1~2n为大数,最终的合法的摸牌序列一定是小数上升序列和大数下降序列的交替,再加上最后的不合法序列

考虑dp

dp[i][j][k]表示填了i个小数j个大数且最后是小/大数的方案数

转移方程易得(k为最后一段数字的个数。)

注意这里没有枚举最后一张错误的但是可以被拿走的牌

dp[i][j][0/1]+dp[i-k][j][1/0]*C(n-i+k,k)

统计答案为

(dp[i][j][0]+dp[i][j][1])*(2*n-i-j)!

最后要算上最后一张拿错的牌的贡献

(2*n)!-(dp[n][n][0]+dp[n][n][1])

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N = 1e7+100;
//const int mod = 1e8;
const int INF = 0x3f3f3f3f;
//ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);

ll n,mod;
ll dp[5000][5000][5];
ll a[10000];
ll c[5000][5000];
ll p[10000];
void init(int n) {
	for(int i=0; i<=n; i++) {
		for(int j=0; j<=n; j++) {
			c[i][j]=0;
		}
	}
	for (int i = 0; i <= n; i++) {
		for (int j = 0; j <= i; j++) {
			c[i][j] = 0 < j && j < i ? (c[i - 1][j - 1] + c[i - 1][j]) % mod : 1;
		}
	}
}
int C(int n, int m) {
	if (n == m && m == -1) return 1;
	if (n < m || m < 0) return 0;
	return c[n][m];
}
void solve() {
	cin>>n>>mod;
	init(2*n+10);
	p[0]=1;
	for(int i=1; i<=2*n; i++) {
		p[i]=(p[i-1]*i)%mod;
	}
	for(int i=0; i<=n; i++) {
		for(int j=0; j<=n; j++) {
			dp[i][j][0]=0;
			dp[i][j][1]=0;
		}
	}
	dp[0][0][0]=1;
	dp[0][0][1]=1;
	ll ans=0;
	for(int i=0; i<=n; i++) {
		for(int j=0; j <=n; j++) {
			for(int k=1; k<=n; k++) {
				if(k<=i)dp[i][j][0]=(dp[i][j][0]+dp[i-k][j][1]*C(n-i+k,k)%mod)%mod;
				if(k<=j)dp[i][j][1]=(dp[i][j][1]+dp[i][j-k][0]*C(n-j+k,k)%mod)%mod;
			}
			if (i+j>0)ans=(ans+(dp[i][j][0]+dp[i][j][1])%mod*p[2*n-i-j]%mod)%mod;
		}
	}
	ans=(ans+p[2*n])%mod;
	ans=(ans-(dp[n][n][0]+dp[n][n][1])%mod+mod)%mod;
	cout<<ans<<'\n';
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t=1;
	cin>>t;
	while(t--) {
		solve();
	}
	return 0;
}

坑点

1.此题算组合数不能用公式法,一开始无脑复制了公式法的板子,随便输出个C(4,2)一直出0,后来才发现因为题目给的模数大部分不是质数,公式法过程中直接出0,这题只能用公式法。

2.init()的c数组忘记清零

3.各种取模问题,加法各处能加的尽量加把,减法不要忘了可能减出个负数来

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心刍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值