CF1204E Natasha, Sasha and the Prefix Sums——DP/数学(组合数)
题面1600174884
解析
题意就是要求所有由nn个11、mm个−1−1构成的序列的最大前缀和的和
算法一(DP)(DP)
nn, mm都小于等于20002000, 显然可以DPDP
设dp[i][j]dp[i][j]表示由ii个11, jj个−1−1构成的序列的最大前缀和的和
ii个11, jj个−1−1构成的序列, 可以看做是在i−1i−1个11, jj个−1−1的序列的最前面加一个11得到,也可以看做是在ii个11, j−1j−1个−1−1的序列最前面加一个−1−1得到
这也就意味着,dp[i][j]dp[i][j]可以由dp[i−1][j]dp[i−1][j]与dp[i][j−1]dp[i][j−1]转移过来
先考虑dp[i−1][j]dp[i−1][j]对dp[i][j]dp[i][j]的贡献,对于任意一种i−1i−1个11, jj个−1−1的序列, 在其首端加入一个11后,最大前缀和都会加11, 序列的个数为C(i+j−1,j)C(i+j−1,j),因此dp[i][j]+=dp[i−1][j]+1∗C(i−j+1,j)dp[i][j]+=dp[i−1][j]+1∗C(i−j+1,j)
在考虑dp[i][j−1]dp[i][j−1]对dp[i][j]dp[i][j]的贡献,这个和上面那个不一样,要麻烦一点。因为对于最大前缀和为00的序列,在其首端加入一个−1−1后,其最大前缀和仍然是00, 所以dp[i][j]+=dp[i][j−1]−1∗(C(i−j+1,j−1)−f[i][j−1])dp[i][j]+=dp[i][j−1]−1∗(C(i−j+1,j−1)−f[i][j−1]), 其中数组f[i][j]f[i][j]表示由ii个11, jj个−1−1构成的所有序列中, 最大前缀和等于00的序列个数
因此问题变成了如何求f数组
与求dpdp数组的思维过程类似。 ii个11, jj个−1−1构成的序列, 可以看做是在i−1i−1个11, jj个−1−1的序列的最后面(注意这里是后面,而dpdp数组是在前面)加一个11得到,也可以看做是在ii个11, j−1j−1个−1−1的序列最后面加一个−1−1得到
对于ff数组,显然有i⩽ji⩽j,那么无论在序列末尾插入11或−1−1, 原来最大前缀和为00的序列在插入11或−1−1后,其最大前缀和依然为00,因此f[i][j]=f[i−1][j]+f[i][j−1]f[i][j]=f[i−1][j]+f[i][j−1]
状态转移方程大概就是这样了
初始化:
f[0][i]=1(1⩽i⩽m)f[0][i]=1(1⩽i⩽m), 其余为00
dp[i][0]=i(1⩽i⩽n)dp[i][0]=i(1⩽i⩽n), 其余为00
答案就是dp[n][m]dp[n][m]
时间复杂度O(NM)O(NM)
在考试中我也定义出来了dpdp, ff数组, 但无论我怎么想就是想不出来转移方程, 结果方程也并不复杂, dpdp还得多加练习啊
代码:
算法二(数学)
这个算法的思路很巧妙, 是从这篇博客中学来的
如果在坐标系中把11看作是向右走一步, −1−1看作是向上走一步, 起点是(0,0)(0,0), 终点是(n,m)(n,m), 那么任意一个序列就会变成从(0,0)(0,0)出发, 只能向右或向上走,走到(n,m)(n,m)的一条路径
设f[i]f[i]为最大前缀和大于等于i的序列个数
那么f[i]f[i]就等于路径中存在一点(x,y)(x,y)满足如下条件的路径数,xx,yy使得i⩽x−yi⩽x−y, 即y⩽x−iy⩽x−i, (1⩽x⩽n,1⩽y⩽m)(1⩽x⩽n,1⩽y⩽m)
结合线性规划的思想,也就是说f[i]f[i]等于路径经过直线y=x−iy=x−i 及其下面区域的路径数
考虑任意一条路径都会到(n,m)(n,m),因此当1⩽i⩽n−m1⩽i⩽n−m 时, f[i]=C(n+m,n)f[i]=C(n+m,n)
而当max(n−m,1)⩽i⩽nmax(n−m,1)⩽i⩽n 时, 我们需要把路径转化一下,起点不再是(0,0)(0,0),而是点(0,0)(0,0)关于直线y=x−iy=x−i的对称点(i,−i)(i,−i),终点不变, 同样是要走n+mn+m步走到(n,m)(n,m),因为只能向右和向上走,那么就一定会经过直线y=x−iy=x−i 及其下面区域。由于我们是把起点对称到了(i,−i)(i,−i),所以从(i,−i)(i,−i)出发经过y=x−iy=x−i与从(0,0)(0,0)出发经过y=x−iy=x−i两种情况的路径是映射的关系,也就是说路径是一一对应的,因此此时f[i]=C(n+m,n−i)f[i]=C(n+m,n−i)。
显然除了上述两种情况外的i, 都有f[i]=0f[i]=0
那么最终的答案为∑ni=1i∗(f[i]−f[i+1])∑i=1ni∗(f[i]−f[i+1])
预处理阶乘与阶乘逆元,以便快速求出组合数
时间复杂度为O(N+M)O(N+M)
路径的转化是这种算法的关键, 也是巧妙所在