题解:CF1608F MEX counting

题解:CF1608F MEX counting

与其他题解不同,本篇题解是运用辅助数组 $g$ 来解决问题。虽然代码可能要繁琐一点,但是辅助数组的思路适用范围更广一点。


首先还是转化为前 $i$ 个数的 $mex$ 在区间 $[l_i,r_i]$ 内。

我们用 dp 数组 $f_{i,x,c}$ 表示处理到了第 $i$ 个数,当前的 mex 为 $x$,大于 mex 一共有 $c$ 个不同的数。这里我们并不关心大于 mex 的数具体是哪些,而只关心有多少个,因为只要满足大于 mex 一共有 $c$ 个不同的数,方案数都是一样的,我们只需要记录这个一样的方案数即可。注意这里钦定了这 $c$ 个数的顺序,即这 $c$ 个数是有序的(看不懂的话可以根据下面的转移来理解)。

根据定义,考虑有哪些方式能转移到 $f_{i,x,c}$:

  • 这个位置填了一个之前已经出现过的数,这样的数共有 $x+c$ 个,所以转移为 $f_{i-1,x,c}*(x+c)\rightarrow f_{i,j,k}$
  • 这个位置填了大于 mex 且之前没有出现过的数,那肯定是由 $f_{i,x,c-1}$ 转移过来,因为原先有 $c-1$ 个数,而数是有序的,所以新加的数与这 $c-1$ 个数的相对顺序共有 $c$ 种,所以转移为 $f_{i,x,c-1}*c\rightarrow f_{i,j,k}$
  • 第三种情况也是本题的核心,即这个位置填了 mex,这样子我们并不知道有哪些状态能更新到。这时就需要辅助数组 $g$,用 $g_{i,x,c}$ 表示填到了第 $i$ 个数,小于 $x$ 的数都出现过,大于等于 $x$ 共有 $c$ 个不同的数。

这样 $g$ 怎么辅助数组 $f$ 来转移呢?具体流程为,先处理前两个转移,同时更新数组 $g$,然后再用 $g$ 来更新 $f$ 的第三个转移。

  • 对于 $f$ 到 $g$ 的转移,即这个位置填了 mex,显然有 $f_{i,x,c}\rightarrow g_{i,x+1,c}$
  • 对于 $g$ 到 $f$ 的转移,那么对 $g_{i,x,c}$ 分两种情况,即是否出现了 $x$ 这个数。如果出现了,那么应该转移 $g_{i,x,c}\rightarrow g_{i,x+1,c-1}$,如果没有出现,那么应该转移 $g_{i,x,c} \rightarrow f_{i,x,c}$

最后统计答案,对于 $f_{n,x,c}$,由于我们没有关心这 $c$ 个数具体是啥,所以要在剩下的 $n-x$ 个数中选 $c$ 个,因为已经钦定了 $c$ 个数的顺序,所以 $f_{n,x,c}$ 对答案的贡献为 $f_{n,x,c}*\binom{n-x}{c}$

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 2005,mod = 998244353;
int l[N],r[N],n,k;
ll C[N][N],f[2][N][N],g[N][N],ans;
void init()
{f[0][0][0] = C[0][0] = 1;for(int i = 1;i <= n;i++)for(int j = 0;j <= i;j++)C[i][j] = ((j?C[i-1][j-1]:0)+C[i-1][j])%mod;
}
inline int rd()
{char c;int f = 1;while(!isdigit(c = getchar()))if(c=='-')f = -1;int x = c-'0';while(isdigit(c = getchar()))x = x*10+(c^48);return x*f;
}
int main()
{// freopen(".in","r",stdin);// freopen(".out","w",stdout);n = rd();k = rd();init();for(int i = 1;i <= n;i++){int x = rd();l[i] = max(l[i-1],x-k);r[i] = min(x+k,n);}for(int i = 1;i <= n;i++){for(int x = l[i-1];x <= r[i];x++)for(int c = 0;c <= n;c++)g[x][c] = 0;for(int x = l[i-1];x <= r[i-1];x++)for(int c = 0;c <= n;c++)f[1][x][c] = f[0][x][c],f[0][x][c] = 0;for(int x = l[i-1];x <= r[i-1];x++)for(int c = 0;c <= n;c++)//mex=x,cnt(1)=c{if(l[i] <= x&&x <= r[i]){(f[0][x][c] += f[1][x][c]*(x+c)) %= mod;if(c)(f[0][x][c] += f[1][x][c-1]*c) %= mod;}(g[x+1][c] += f[1][x][c]) %= mod;}for(int x = l[i-1];x <= r[i];x++)for(int c = 0;c <= n;c++){if(c)(g[x+1][c-1] += g[x][c]) %= mod;if(l[i] <= x&&x <= r[i])(f[0][x][c] += g[x][c]);}}for(int x = 0;x <= n;x++)for(int c = 0;c <= n;c++)(ans += f[0][x][c]*C[n-x][c]) %= mod;cout << ans;return 0;
}

这种设辅助数组的思路在许多题中都可以用,值得掌握一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/772646.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode-5

题目: 给你一个字符串 s,找到 s 中最长的 回文子串 示例 1:输入:s = "babad"  输出:"bab"  解释:"aba" 同样是符合题意的答案。 示例 2:输入:s = "cbbd"   输出:"bb" 提示: 1 <= s.length <= 1000…

Uniapp 之手写签名

一、效果图二、代码示例 qianming.jsexport const qianming = {data() {return {windowWidth: 0,pixelRatio: 0,context: null,points: [],oldPoints: [],qm_width: 280,qm_height: 120,qm_img: }},methods: {qm_start() {const systemInfo = uni.getSystemInfoSync()this.wind…

代码随想录day11 || 150 逆表达式求值 239 滑动窗口最大值 347 前k最高频元素

150 逆波兰表达式计算func evalRPN(tokens []string) int {// 自己想是真的想不出来,看了视频之后有了思路// 本质上逻辑就是遇到数字入栈,遇到运算符号 出栈两个元素然后计算再入栈,最终就是计算结果stack := Constructor()for _, val := range tokens{// 如果数字入栈if i…

计算机组成与体系结构-校验码

奇偶校验码 奇偶校验是一种简单有效的校验方法,这种方法通过在编码中增加一位校验位来使编码中1的个数为奇数(奇校验)或者为偶数(偶校验),只能发现奇数个数据位 出错的情况.循环冗余校验码 CRC(Cyclic RedundancyCheck)循环冗余校验是一种常用的错误检测技术,用于在数据传输…

CAD快捷键命令大全(最全)

勇者无惧,强者无敌。

电影《抓娃娃》迅雷/百度云下载[超清版BT种子][MP4/2.89GB]分享

电影《抓娃娃》是一部由闫非、彭大魔联合执导,沈腾、马丽领衔主演的喜剧电影。该片于2024年7月16日在中国大陆正式上映,以其独特的剧情设定和深刻的主题探讨,迅速吸引了广大观众的关注。影片不仅延续了“沈马组合”一贯的幽默风格,更在喜剧外壳下包裹了深刻的教育主题,让人…

PWM波形生成

背景 方法 定时器 (1)高级定时器timer1, timer8以及通用定时器timer9, timer10, timer11的时钟来源是APB2总线 (2)通用定时器timer2~timer5,通用定时器timer12~timer14以及基本定时器timer6,timer7的时钟来源是APB1总线 (3)当APB1和APB2分频数为1的时候,TIM1、TIM8~TIM…

ComfyUI插件:ComfyUI Impact 节点(一)

前言: 学习ComfyUI是一场持久战,而 ComfyUI Impact 是一个庞大的模块节点库,内置许多非常实用且强大的功能节点 ,例如检测器、细节强化器、预览桥、通配符、Hook、图片发送器、图片接收器等等。通过这些节点的组合运用,我们可以实现的工作有很多,例如自动人脸检测和优化修…

ComfyUI进阶:Comfyroll节点 (最终篇)+应用实例

前言: 学习ComfyUI是一场持久战,而Comfyroll 是一款功能强大的自定义节点集合,专为 ComfyUI 用户打造,旨在提供更加丰富和专业的图像生成与编辑工具。借助这些节点,用户可以在静态图像的精细调整和动态动画的复杂构建方面进行深入探索。Comfyroll 的节点设计简洁易用,功能…

松灵机器人scout mini小车 自主导航(4)——运行lio-sam建图

松灵机器人Scout mini小车运行lio-sam 在之前的工作中,我们已经实现了用小车搭载传感器,采用gmapping建图和navigation导航实现小车在2D环境中自主导航,但是实际我们采用的激光雷达多为三维激光雷达。因此决定采用lio-sam来建图。具体操作步骤如下。 1.下载雷达仿真 1.1下载…

[题解]P2672 [NOIP2015 普及组] 推销员

P2672 [NOIP2015 普及组] 推销员 为了便于操作,将住户信息按疲劳值从大到小排序。 那么对于选\(X\)个住户,有\(2\)种情况:选疲劳值前\(X\)大的住户,答案即为\(\sum\limits_{i=1}^X a[i] + 2\times \max\limits_{i=1}^X s[i]\)。 选疲劳值前\(X-1\)大的住户,然后在剩下的住…

LockSupport

LockSupprot 用来阻塞和唤醒线程,底层实现依赖于 Unsafe 类(后面会细讲)。 该类包含一组用于阻塞和唤醒线程的静态方法,这些方法主要是围绕 park 和 unpark 展开。 public class Main {public static void main(String[] args) {Thread mainThread = Thread.currentThread(…