[CF1204E]Natasha,Sasha and the Prefix Sums 题解
前言
本文中的排列指由n个1, m个-1构成的序列中的一种。
题目这么长不吐槽了,但是这确实是一道好题。
题解
DP题话不多说,直接状态/变量/转移。
状态
我们定义f表示"最大prefix sum"之和
变量
f[i][j]为有i个1,j个-1的"最大prefix sum"之和
转移
我们记C[i][j]为\(\left(\begin{matrix} i \\ j\end{matrix}\right)\),那么:
\[f[i][j] = \left\{\begin{matrix} f[i-1][j]+1 \times C[i+j-1][i-1] \\ f[i][j-1]+(-1)\times(C[i+j-1][j-1]-k[i][j-1])\end{matrix}\right.\]
k[i][j]表示有i个1,j个-1的最大前缀和刚好为0的排列的个数
那么上式是如何推出的呢?
我们固定地认为每当新加入一个数的时候将该数插入序列的最前方,这种设定仍然保证了动规涵盖所有珂能的排列。
如果我们插入的是一个1,不管先前的序列排列如何,最大prefix sum一定会加1,由于i-1个1,j个-1对应的序列有\(\left(\begin{matrix} i+j-1 \\ i\end{matrix}\right)\)种排列方法,所以当前状态增加的贡献为\(\left(\begin{matrix} i+j-1 \\ i\end{matrix}\right)\)。
如果我们插入的是一个-1,情况于上面是完全相同的,但是注意到,如果有一种排列它本身的"最大prefix sum"为0,那么我们不应当把它计入贡献(因为"最大prefix sum"最小为0),所以要减去k[i][j]。
组合数显然珂以通过杨辉三角递推解决。
那么现在我们的问题就在于k[i][j]如何处理。
我们先给出k[i][j]的递推式。
\[k[i][j]=\left\{\begin{matrix}i=0 & k[i][j]=1 \\ j=0 & k[i][j]=0 \\ i > j & k[i][j]=0 \\ \text{其余情况} & k[i][j]=k[i-1][j]+k[i][j-1]\end{matrix}\right.\]
这个递推式珂能有点晦涩,但是一种简单的理解方式是找出由当前状态向外转移的方程式,然后再转化为以上方程式。
于是我们解决了此题。
代码
没有卡常,见谅。
#include <cstdio>
#define MOD 998244853
long long f[2005][2005];
long long k[2005][2005];
long long C[4005][4005];
int main(){
int n, m; scanf("%d %d", &n, &m);
for (register int i = 0; i <= n; ++i)
for (register int j = 0; j <= m; ++j){
if (i == 0) k[i][j] = 1;
else if (j == 0) k[i][j] = 0;
else if (i > j) k[i][j] = 0;
else k[i][j] = (k[i - 1][j] + k[i][j - 1]) % MOD;
}
C[0][0] = C[1][0] = C[1][1] = 1;
for (register int i = 2; i <= n + m; ++i){
C[i][0] = 1;
for (register int j = 1; j <= i; ++j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
}
for (register int i = 0; i <= n; ++i)
f[i][0] = i, f[0][i] = 0;
for (register int i = 1; i <= n; ++i)
for (register int j = 1; j <= m; ++j)
f[i][j] = ((f[i - 1][j] + C[i + j - 1][i - 1]) % MOD + (f[i][j - 1] - C[i + j - 1][j - 1] + k[i][j - 1] + MOD) % MOD) % MOD;
printf("%I64d", f[n][m]);
return 0;
}
原文地址:https://www.cnblogs.com/linzhengmin/p/11391922.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Angular Observable数据类型的单元测试数据准备
- 第007课 裸机开发步骤和工具使用(SourceInght NotePad++使用)
- Angular jasmine.expect单步调试
- 第008课 第1个ARM裸板程序及引申(点亮LED灯)
- SharedPreferences VS MMKV
- 第009课 gcc和arm-linux-gcc和Makefile
- Go 每日一库之 quicktemplate
- 第010课 掌握Jz2440_ARM芯片时钟体系
- 第011课 Jz2400串口(UART)的使用
- 面试官看完我手写的单例直接惊呆了!
- 安利几个JS开发小技巧
- 深入理解Pod(一)
- [902]python list排序
- 第012课 内存控制器与SDRAM
- 一篇文章教给你Bypass学习基础