0%

CF Edu 170 E - Card Game

题目大意

题面

有一副牌,共 n×mn \times m 张。每张牌有两个参数:花色和点数。

  • 花色编号为 1n1 \ldots n

  • 点数编号为 1m1 \ldots m

  • 每一种花色和点数的组合都恰好有一张牌。

一张牌 (a,b)(a,b) 可以打败另一张牌 (c,d)(c,d) ,当且仅当满足下面两种情况之一:

  • a=1a=1c1c\ne 1 。也就是说,花色 11 是王牌,可以打败任何非王牌花色。

  • a=ca=cb>db>d 。也就是说,同花色里只能用更大的点数去打更小的点数。

两名玩家把整副牌平分。第一名玩家获胜,当且仅当:对第二名玩家的每一张牌,都能选出第一名玩家的一张牌打败它,并且第一名玩家的每张牌最多只用一次。

题目要求统计有多少种分牌方式能让第一名玩家获胜,答案对 998244353998244353 取模。

输入格式

输入一行两个整数 n,mn,m

输出格式

输出一个整数,表示合法分牌方式数量。

数据范围

  • 1n,m5001 \le n,m \le 500

  • mm 是偶数

样例

1
2
3
4
Input
1 4
Output
2
1
2
3
4
Input
2 2
Output
2
1
2
3
4
Input
3 6
Output
1690
1
2
3
4
Input
5 4
Output
568
1
2
3
4
Input
500 500
Output
84693741

思路讲解

一句话

这题先把一个花色内部压成一排 Catalan / ballot 数,再把非王牌花色的“缺口”当成资源消耗做 DP。

关键不变量:花色 11 多出来的牌,必须刚好补完所有非王牌花色内部的缺口。

一个花色为什么像括号序列

先只看一个固定花色,把点数从大到小扫。

  • 第一名玩家拿到这张牌,记成左括号。

  • 第二名玩家拿到这张牌,记成右括号。

同花色牌只能用高点数打低点数,所以扫到任何一个高点数前缀时,都必须保证这个前缀里第一名玩家的牌数不少于第二名玩家的牌数。

换成括号语言,就是:

任意前缀里,左括号数量不能少于右括号数量。

这就是 Catalan 数最常见的前缀余额模型。

Catalan 数和反射法

如果一个花色里双方各拿 m/2m/2 张,并且完全靠本花色自己匹配,那么合法安排数是:

(mm/2)(mm/2+1)\binom{m}{m/2}-\binom{m}{m/2+1}

它来自反射法:所有平衡括号序列一共有 (mm/2)\binom{m}{m/2} 个;不合法序列会在某个位置第一次让前缀余额变成 1-1 ,把开头到这个位置的括号全部翻转,就会一一对应到左括号多一个的序列,也就是 (mm/2+1)\binom{m}{m/2+1} 个。

所以合法数就是“全部方案减掉坏方案”。

带缺口的 ballot 数

这题里非王牌花色不一定要自己完全匹配,因为花色 11 可以来救场。

如果某个非王牌花色中,第一名玩家比一半少拿了 jj 张,那么:

  • 第一名玩家拿了 m/2jm/2-j 张。

  • 第二名玩家拿了 m/2+jm/2+j 张。

  • 这个花色总量上缺了 2j2j 张先手牌。

为了不额外消耗更多王牌,这个花色从大到小扫描时,前缀余额最低只能掉到 2j-2j 。这仍然是 ballot 数,方案数刚好是:

dp1[j]=(mm/2+j)(mm/2+j+1)dp1[j]=\binom{m}{m/2+j}-\binom{m}{m/2+j+1}

这个式子也能用于王牌花色:如果花色 11 中第一名玩家比一半多拿了 jj 张,那么它自己内部能匹配,并且最后剩下 2j2j 张王牌去补其它花色。

状态定义怎么长出来

单个花色已经被压成了 dp1[j]dp1[j] ,后面就不用再记每张牌的具体归属了。

可以定义 dp2[j]dp2[j] 为:处理完若干个花色后,花色 11 还剩 jj 份优势没有被用掉的方案数。

这里的 jj 表示“多拿了几张的一半资源单位”。真实可补的王牌数量是 2j2j

初始化时,只处理花色 11

dp2[j]=dp1[j]dp2[j]=dp1[j]

然后依次处理其它 n1n-1 个花色。假设当前还剩 kk 份优势,当前非王牌花色少拿了 kjk-j 张,那么处理后就还剩 jj 份优势:

ndp[j]+=dp2[k]dp1[kj]ndp[j] += dp2[k]\cdot dp1[k-j]

也就是代码里的转移:

1
2
3
4
5
6
for (int j = 0; j <= M / 2; ++j) {
for (int k = j; k <= M / 2; ++k) {
ndp[j] += dp2[k] * dp1[k - j];
ndp[j] %= mod;
}
}

最后必须剩余优势为 00 ,因为整副牌要平分,花色 11 多拿的量必须被非王牌少拿的量完全抵消:

1
ll ans = dp2[0];

复杂度

m500m\le 500 ,所以状态上限只有 m/2m/2

  • 预处理组合数: O(m)O(m)

  • 单花色贡献: O(m)O(m)

  • 多花色 DP: O(nm2)O(nm^2)

  • 空间复杂度: O(m)O(m)

AC 代码

AC 提交链接

源码较长,折叠如下。

心路历程(WA,TLE,MLE……)

卡点:第二维不应该是点数位置

一开始很容易想成 dp[i][j]dp[i][j] 表示“处理到第 ii 个花色、第 jj 张牌”。这个方向的问题是:花色内部一旦处理完,跨花色传递的并不是具体点数位置,而是这个花色消耗了多少王牌资源。

真正要传递的是“花色 11 还剩多少优势”,或者等价地,“非王牌花色累计少拿了多少”。所以第二维应该是资源缺口,不是 rank 下标。

卡点:每一轮 DP 必须清空

处理一个新花色时,ndp 必须从 00 开始。

1
ndp.assign(M / 2 + 2, 0);

如果写成 ndp = dp2,就相当于允许当前花色不处理,旧状态直接继承,会把方案数多算进去。

卡点:答案不是所有剩余状态求和

如果 dp2[j]dp2[j] 表示花色 11 还剩 jj 份优势,那最后只能取 dp2[0]dp2[0]

剩余优势大于 00 的状态,表示花色 11 多拿的牌没有被其它花色少拿的牌抵消,整副牌没有平分,不能算合法分配。

附件

暂无。