一个计数问题(cf1295F)

Educational Codeforces Round 81 Problem F. Good Contest

题意

一个长度为(n)的整数数列,第(i)个元素在([l_i, r_i])里均匀随机,求它单调不增的概率。由于答案是个有理数,输出它在模(998244353)下的值。

题面里定义的inversion很奇怪,所以我一开始把它看成单调不减了,测样例才发现。不过也没关系,把序列翻转一下就行,后面就按单调不减写了。

(l_i, r_i)的范围很大:(0 leq l_i leq r_i leq 998244351),仅仅让作为分母的(r_i-l_i+1)不达到(998244353)以保证逆元存在。(n leq 50)

观察

首先考虑朴素的dp:(dp[i][j])表示第(i)个元素是(j),并且以它为结尾的前缀单调不减的方案数,转移是平凡的:(dp[i][j]=sum_{l_{i-1} leq k leq j} dp[i-1][k])

注意到(n)不是很大,那么离散化之后值域就被分成了(O(n))段。按这个思路做下去的话,就是要考虑每一段中的dp值有什么性质,一起处理。为了找到这个性质,首先观察一个简单的情况:所有区间均相同,(l=0)(r)充分大。这时考虑差分,(dp[i][j])就是将(j)拆分为(i)个非负整数之和的方案数了,即(dp[i][j]=egin{pmatrix} j+i-1 \ i-1 end{pmatrix})

再考虑上面的dp转移,这提示我们用到组合数的这个性质:(sum_{k=0}^r egin{pmatrix} t+k \ t end{pmatrix} = egin{pmatrix} t+r+1 \ t+1 end{pmatrix}),这意味着如果值域中某一段的dp值为(egin{pmatrix} t \ t end{pmatrix}, egin{pmatrix} t+1 \ t end{pmatrix}, dots),那么它对下一阶段同一段的贡献依然保持这一形式,不过(t)会增加(1)

除了上一阶段同一段的贡献,上一阶段更小的段也会产生贡献:使当前段所有dp值加上一个常数,这可以被看成(t=0)的情况乘上一个常数。因此,一段的dp值可以被拆分为若干个(cegin{pmatrix} t \ t end{pmatrix}, cegin{pmatrix} t+1 \ t end{pmatrix}, dots)的和的形式,并且在转移中依然保持这个形式。其实也就是归纳证明了这个表示方法。

这样一来其实已经做出来了,对每个阶段每个值域段存储一个(c-t) pair的列表即可。在转移时,直接把上一阶段同一值域段的列表复制过来,所有(t)(1),再增加一项更小的值域段的贡献。这样,列表的长度每过一个阶段至多增加(1),是(O(n))的。转移时从小到大枚举值域段,累加前一阶段贡献的同时算出这一阶段。这里要做的是把前一阶段当前值域段的dp值之和累加进去。一个(c-t) pair对应的和是一个组合数,经过(O(n^2))的预处理是可以(O(1))做的。因此对于(O(n))段,每段枚举(O(n))(c-t) pair,每阶段耗时(O(n^2))。同值域段转移时复制vector也是一样的复杂度,所以复杂度是(O(n^3))

因为(n)很小,我比赛的时候并没有预处理组合数,复杂度实际是(O(n^4))的,依然跑得飞快。

原文地址:https://www.cnblogs.com/lkmcfj/p/12248081.html