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:
- Draw a card from the top of the deck.
- 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.
- 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.各种取模问题,加法各处能加的尽量加把,减法不要忘了可能减出个负数来