https://codeforces.com/contest/1739/problem/C
题意
一共 n 张卡片,每张卡片上标有一个数字,标号从 1 到 n 各不相同,分给 Alice 和 Bob 各 n/2 张。
一共 n/2 轮操作,Alice 和 Bob 两个玩家轮流作为先手,初始 Alice 作为第一轮先手。
对于每一轮来说:
如果 n/2 轮结束时,未出胜负,那么平局。
两者都按最优策略操作。
问,最终在 Alice赢,Bob赢,平局 三种情况下,n 张卡片的分配方案各有多少?
思路
首先需要知道,一个玩家如果作为先手的话,一定会打出其拥有的最大牌,要么直接胜利,要么把对方更大的牌引出来,尽量保证自己下一局的安全。
然后尝试讨论所有情况:
如果 Alice 有最大牌 n,Alice 在第一轮就赢,分配方案 C n − 1 n 2 − 1 C_{n-1}^{\frac n 2-1} Cn−12n−1;
否则 Bob 有最大牌 n:
然后根据样例发现,平局的情况只有一种,两个人轮流分牌的情况,所以可以把 Alice 赢的所有方案求出来,用总方案数减去平局和Alice赢的方案数就是Bob赢的方案数。
如果定义 i
个卡牌时,Alice赢的方案数一共 f[i]
,那么对于 n 个卡牌时,Alice 赢的方案数就是:
C
n
−
1
n
2
−
1
+
C
n
−
4
n
2
−
3
+
f
[
n
−
4
]
C_{n-1}^{\frac n 2-1} + C_{n-4}^{\frac n 2-3} + f[n-4]
Cn−12n−1+Cn−42n−3+f[n−4]。
从小到大遍历所有 i,用前面的答案更新当前答案。
初始化 f[2] = 1。
Code
#include
using namespace std;
#define Ios ios::sync_with_stdio(false),cin.tie(0)
const int N = 200010, mod = 998244353;
int T, n, m;
int C[70][70];
int f[N];
signed main(){
Ios;
for(int i=0;i<=60;i++)
for(int j=0;j<=60;j++)
{
if(!j) C[i][j] = 1;
else C[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod;
}
f[2] = 1;
for(int i=2;i<=60;i++)
{
if(i % 2) continue;
f[i] = (C[i-1][i/2 - 1] + C[i-4][i/2 - 3] + f[i - 4]) % mod;
}
cin >> T;
while(T--)
{
cin >> n;
cout << f[n] << " " << (C[n][n/2] - f[n] - 1 + mod) % mod << " " << 1 << endl;
}
return 0;
}
场上没有把所有情况都考虑清楚,只想到次大值就开始求方案,想把方案数一次性求出来,然后发现求不出来。。
完全没有想到递推。。