Codeforces Round #581 (Div. 2)-E. Natasha, Sasha and the Prefix Sums-动态规划+组合数学

Codeforces Round #581 (Div. 2)-E. Natasha, Sasha and the Prefix Sums-动态规划+组合数学


【Problem Description】

? 给你\(n\)个\(1\),\(m\)个\(-1\),他们任意排列有\(\frac{(n+m)!}{n!\cdot m!}\)中排列,每种排列都有一个最大前缀和(可能为\(0\)),求所有排列的最大前缀和之和为多少。

【Solution】

? 定义\(dp[i][j]\)表示有\(i\)个\(1\),\(j\)个\(-1\)时所有排列的最大前缀和之和为\(dp[i][j]\)。则状态转移方程为:\(dp[i][j]=dp[i-1][j]+C_{i+j-1}^{j}+dp[i][j-1]-C_{i+j-1}^{i}+k[i][j]\)。其中\(k[i][j]\)表示有\(i\)个\(1\),\(j\)个\(-1\)时最大前缀和为\(0\)的排列的个数。

? 其中\(dp[i-1][j]+C_{i+j-1}^j\)表示新增加一个数字\(1\)时,将其放到所有排列的最前端,则所有排列的最大前缀和都增加\(1\),且总共有\(C_{i+j-1}^j\)种排列,所以在原来基础上增加了\(C_{i+j-1}^j\)。

? \(dp[i][j-1]-C_{i+j-1}^i+k[i][j]\)表示新增加一个数字\(-1\)时,同上所有排列的最大前缀和减少\(1\),除了最大前缀和为\(0\)的排列。

? 那为什么\(i-1\)个\(1\),\(j\)个\(-1\)的所有排列个数为\(C_{i+j-1}^j\)呢?是因为根据多重集合的排列公式\(\frac{((i-1)+j)!}{(i-1)!\cdot j!}\)得到的。

? 最大前缀和\(k[i][j]\)怎么求呢?\(k[i][j]=k[i-1][j]+k[i][j-1]\)。表示在保证\(i\le j\)的情况下,新增加一个数字\(1\),将其放到末端的排列数加上新增加一个\(-1\),将其放在末端的排列数。

? 最后一个问题,为什么\(dp\)数组中\(1,-1\)要放在前端,\(k\)数组中\(1,-1\)要放在末端,并且为什么不能同时放在前端和后端以及任意其他位置?\(dp\)数组中放在前端是因为要求最大前缀和最大,比如将多出的\(1\)放在后端一定不可能使得最大前缀和最大,\(k\)数组中放在后端是因为要求最大前缀和为\(0\),比如将多出的\(1\)放在前端,那么最大前缀和最小就等于\(1\)。

? 不能同时放在其他位置是因为只要放一个位置就包含了所有可能的排列,若同时放在其他位置就重复计算了可能出现的排列数。例如有\(i\)个\(1\),\(j\)个\(-1\),可知它们的所有排列个数为\(C_{i+j}^j\),若有\(i-1\)个\(1\),\(j\)个\(-1\),则共有的排列个数为\(C_{i+j-1}^j\),若有\(i\)个\(1\),\(j\)个\(-1\),则共有的排列个数为\(C_{i+j-1}^i=C_{i+j-1}^{j-1}\)。它们之间的关系就是:
\[
C_{i+j}^{j}=C_{i+j-1}^{j}+C_{i+j-1}^{j-1}
\]
由帕斯卡公式可知上式一定成立。所以可以知道由数组的含义决定了放在哪个位置,由上述关系决定了只能放\(1\)个位置。


【Code】

/*
 * @Author: Simon
 * @Date: 2019-08-28 19:32:35
 * @Last Modified by: Simon
 * @Last Modified time: 2019-08-28 20:26:23
 */
#include<bits/stdc++.h>
using namespace std;
typedef int Int;
#define int long long
#define INF 0x3f3f3f3f
#define maxn 2005
const int mod=998244853;
int dp[maxn][maxn]/*i个1,j个-1的所有排列的最大前缀和之和为dp[i][j]*/,k[maxn][maxn]/*i个1,j个-1的所有排列的最大前缀和为0的个数*/;
int bit[maxn<<1],C[maxn<<1][maxn<<1]/*组合数组*/;
Int main(){
#ifndef ONLINE_JUDGE
    //freopen("input.in","r",stdin);
    //freopen("output.out","w",stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;cin>>n>>m;
    bit[0]=1;C[0][0]=1;
    for(int i=1;i<=n+m;i++) bit[i]=bit[i-1]*i%mod,C[i][0]=1;
    for(int i=1;i<=m;i++) k[0][i]=1;
    for(int i=1;i<=n+m;i++){ //预处理
        for(int j=1;j<=n+m;j++){
            if(j>=i&&i<=n&&j<=m) k[i][j]=(k[i-1][j]+k[i][j-1])%mod;
            if(i>=j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
    }
    for(int i=1;i<=n;i++) dp[i][0]=i;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            dp[i][j]=((dp[i-1][j]+C[i+j-1][j]+dp[i][j-1]-C[i+j-1][i]+k[i][j-1])%mod+mod)%mod;
        }
    }
    cout<<(dp[n][m]+mod)%mod<<endl;
#ifndef ONLINE_JUDGE
    cout<<endl;system("pause");
#endif
    return 0;
}

原文地址:https://www.cnblogs.com/--Simon/p/11429113.html

时间: 2024-07-30 14:02:28

Codeforces Round #581 (Div. 2)-E. Natasha, Sasha and the Prefix Sums-动态规划+组合数学的相关文章

Codeforces Round #425 (Div. 2) Problem A Sasha and Sticks

It's one more school day now. Sasha doesn't like classes and is always bored at them. So, each day he invents some game and plays in it alone or with friends. Today he invented one simple game to play with Lena, with whom he shares a desk. The rules

Codeforces Round #581 (Div. 2)D(思维,构造,最长非递减01串)

#define HAVE_STRUCT_TIMESPEC#include<bits/stdc++.h>using namespace std;char s[100007];int main(){ cin>>s+1; int n=strlen(s+1); int cnt=0; for(int i=n;i>=1;--i){//从后向前,保证后面的解都是合法的情况下 if(s[i]=='1'){//如果当前位置的数字是1 if(cnt)//i后面1的个数小于0的个数,此时如果把i位

Codeforces Round #581 (Div. 2)

A.BowWow and the Timetable 题目连接 题目大意 给你一个以二进制方式来表示数字\(n\)的字符串,问\(4^k < n\)数字的个数. 解题思路 题目给的数字是以二进制的方式给的,那么结合二进制数字的特点,可以发现每个\(4^k\)用二进制表示最前面的那个1都是在奇数位置,那么如果给的字符串长度是偶数,直接除以2:如果是奇数的话,加1除以2,并且判断最高位1是否满足条件. AC代码 #include <bits/stdc++.h> using namespace

CodeForces - 1204E Natasha, Sasha and the Prefix Sums (组合数学,卡特兰数扩展)

题意:求n个1,m个-1组成的所有序列中,最大前缀之和. 首先引出这样一个问题:使用n个左括号和m个右括号,组成的合法的括号匹配(每个右括号都有对应的左括号和它匹配)的数目是多少? 1.当n=m时,显然答案为卡特兰数$C_{2n}^{n}-C_{2n}^{n+1}$ 2.当n<m时,无论如何都不合法,答案为0 3.当n>m时,答案为$C_{n+m}^{n}-C_{n+m}^{n+1}$,这是一个推论,证明过程有点抽象,方法是把不合法的方案数等价于从(-2,0)移动到(n+m,n-m)的方案数,

E. Natasha, Sasha and the Prefix Sums

给定n个 1 m个 -1的全排 求所有排列的$f(a)=max(0,max_{1≤i≤l}∑_{j=1}^{i}a_{j})$之和 组合数,枚举 #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll MOD = 998244853; int n, m; ll C[4002][4002]; ll sum; ll realSum; ll ans; void init() { for(int i=0;

[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

CF1024E Natasha, Sasha and the Prefix Sums——DP/数学(组合数)

题面 CF1024E 解析 题意就是要求所有由$n$个$1$.$m$个$-1$构成的序列的最大前缀和的和 算法一$(DP)$ $n$, $m$都小于等于$2000$, 显然可以$DP$ 设$dp[i][j]$表示由$i$个$1$, $j$个$-1$构成的序列的最大前缀和的和 $i$个$1$, $j$个$-1$构成的序列, 可以看做是在$i-1$个$1$, $j$个$-1$的序列的最前面加一个$1$得到,也可以看做是在$i$个$1$, $j-1$个$-1$的序列最前面加一个$-1$得到 这也就意味

CF1204E Natasha, Sasha and the Prefix Sums

题意 给\(n\)个1和\(m\)个0,定义一个01串的权值为它所有前缀和的最大值(包括0),求可以组成的所有不同串的权值和,答案对998244853取模 思路 由于数据较小,本题有个\(O(n^2)\)比较复杂的DP做法,自行百度... 实际上本题用数学规律可以\(O(n)\)做 设\(f_i\)表示权值为\(i\)的01串数量,直接求不容易,再设\(g_i\)为权值至少为\(i\)的01串数量,那么\(f_i=g_i-g_{i+1}\) 利用求卡特兰数列的一种方法:将01串看做从坐标系\((

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i