吃水果

吃水果

n 个小朋友站成一排,等着吃水果。

一共有 m 种水果,每种水果的数量都足够多。

现在,要给每个小朋友都发一个水果,要求:在所有小朋友都拿到水果后,恰好有 k 个小朋友拿到的水果和其左边相邻小朋友拿到的水果不同(最左边的小朋友当然不算数,即最左边的小朋友不包含在 k 个小朋友内)。

请你计算,一共有多少种不同的分发水果的方案。

输入格式

一行,三个整数 n,m,k

输出格式

一个整数,表示合理的分发水果的方案总数量对 998244353 取模后的结果。

数据范围

5 个测试点满足 1n,m5
所有测试点满足 1n,m20000kn1

输入样例1:

1 3 3 0

输出样例1:

3

输入样例2:

3 2 1

输出样例2:

2

 

解题思路

  求方案数,想到用动态规划。一开始定义的状态是f(i,j,k),表示为前i个人分配好水果,且第i个人分配到水果j,且一共有k个人的水果与左边的人不一样。状态转移方程就是f(i,j,k)=f(i1,j,k)+u=1 && ujmf(i1,u,k1)

  这样做应该是没什么问题的,但很明显这种做法必定会超时,因为状态转移的时间复杂度为O(nm2k)

  事实上可以把第二维的状态给优化掉,定义状态f(i,j)表示前i个人中恰好有j个人拿到的水果与左边的人不一样。根据第i个人拿到的水果与左边第i1个人是否相同来进行状态划分,状态转移方程用到了组合计数f(i,j)=f(i1,j)+f(i1,j1)×(m1)

  对于第i个人拿到的水果与左边第i1个人相同这种情况,意味着前i1个人中恰好有j个人拿到的水果与左边的人不一样,对应的方案是f(i1,j)。又因为第i个人与第i1个人相同,因此这个集合方案数为f(i1,j)×1=f(i1,j)

  对于第i个人拿到的水果与左边第i1个人不相同这种情况,意味着前i1个人中恰好有j1个人拿到的水果与左边的人不一样,对应的方案是f(i1,j1)。对于前面任何一种方案固定之后(指前i1个人有j1个不同),因为第i个人与第i1个人不相同,因此第i个人有m1种选法,因此这个集合方案数为f(i1,j1)×(m1)

  可以发现f(i1,j1)×(m1)是对原先定义的状态f(i,j,k) u=1 && ujmf(i1,u,k1) 的优化。

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 2010, mod = 998244353;
 5 
 6 int f[N][N];
 7 
 8 int main() {
 9     int n, m, k;
10     scanf("%d %d %d", &n, &m, &k);
11     
12     f[1][0] = m;
13     for (int i = 2; i <= n; i++) {
14         for (int j = 0; j <= k && j < i; j++) {
15             f[i][j] = f[i - 1][j];
16             if (j) f[i][j] = (f[i][j] + f[i - 1][j - 1] * (m - 1ll)) % mod;
17         }
18     }
19     
20     printf("%d", f[n][k]);
21     
22     return 0;
23 }
复制代码

  还可以通过组合计数来求出答案,答案就是Cn1k×m×(m1)k

  首先由于第一个人不算数,因此我们从后面的n1个人选出k个人,这k个人与其左边相邻的人拿到的水果不同,因此有Cn1k种选法。

  然后第一个人可以选择m种水果。nk1个人与其左边相邻的人拿到的水果相同,因此只有1种选择,即其左边的人拿什么水果这个人就拿什么水果。k个人每个人与其左边相邻的人拿到的水果不同,因此有m1种选择,k个人就有(m1)k种选择。

  最后通过乘法原理得到答案。

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int mod = 998244353;
 5 
 6 int qmi(int a, int k) {
 7     int ret = 1;
 8     while (k) {
 9         if (k & 1) ret = 1ll * ret * a % mod;
10         a = 1ll * a * a % mod;
11         k >>= 1;
12     }
13     return ret;
14 }
15 
16 int main() {
17     int n, m, k;
18     scanf("%d %d %d", &n, &m, &k);
19     int ret = 1ll * m * qmi(m - 1, k) % mod;
20     for (int i = 1, j = n - 1; i <= k; i++, j--) {
21         ret = 1ll * ret * j % mod * qmi(i, mod - 2) % mod;
22     }
23     printf("%d", ret);
24     
25     return 0;
26 }
复制代码

 

参考资料

  AcWing 4496. 吃水果(AcWing杯 - 周赛):https://www.acwing.com/video/4128/

posted @   onlyblues  阅读(81)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示