[dp] [组合计数] [神仙题] ARC146e Simple Speed

显然这是 $\rm dp$。

容易想到一个以 $B$ 序列长度定义的做法,但是没法优化。

改变角度,从值域入手,即将元素从小到大依次插入 $B$ 序列,这样的好处是元素大小有序,即新插入的元素一定大于原来插入的元素。

考虑插入型 $dp$,定义 $f_{i,j}$ 表示考虑 $[1,i]$ 的元素构成了 $j$ 个合法区间的方案数,注意到每个区间的左边界和右边界必须为 $i$,否则此后无法被合并成大的合法区间。

设当前插入 $i$,枚举 $f_{i-1,j}$。那么每个合法区间的间隙都必须插入一个 $i$,然后我们随意将剩下的 $a_i-(j-1)$ 个 $i$ 安排在这些间隙,这是个经典问题,方案数是可以算出来的。最后考虑插入完毕后的合法区间数,易发现安排完 $j-1$ 个 $i$ 后,剩下的所有 $i$ 要么将一个合法区间断开成为两个,要么独自构成一个合法区间,总的来说就是每添加一个 $i$ 就会使得区间数 $+1$,因此最终区间数为 $a_i-(j-1)+1$。

但是这样会少算一些方案,我们注意到最左 / 最右区间的左边 / 右边不一定要是 $i$,于是完善状态: $f_{i,j,0/1,0/1}$ 表示考虑最左边是否为 $i$ 和最右边是否为 $i$,若钦定某一边必须放 $i$,就在一开始分配一个 $i$ 给它,并且之后随意分配时允许在这一边放 $i$。

理清思路后,转移方程就很简单了,建议自己手推。

至此,复杂度为 $O(n\sum a_i)$,不过容易发现 $j$ 这一维必须 $\le a$,否则此后不可能将合法区间合并为一个区间,复杂度 $O(n^2)$。

然后怎么办?如果你将状态转移画成图,就能发现对于每个 $i$,可能有值的 $f_{i,j}$ 不超过 $3$ 个,这道题神就神在这了。于是拿个 mapset 保存状态即可。

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define ADD(a, b) (a) = ((a) + (b)) % mod
const int N = 2e5 + 5, mod = 998244353;
int n, a, f[N][2][2], f2[N][2][2];
int jc[N << 1], jcinv[N << 1];
set<int>now, now2;

inline int qstp(int a, int k) {int res = 1; for(; k; a = a * a % mod, k >>= 1) if(k & 1) res = res * a % mod; return res;}
inline void init() {jcinv[0] = jc[0] = 1; for(int i = 1; i < N << 1; ++i) jcinv[i] = qstp(jc[i] = jc[i - 1] * i % mod, mod - 2);}
inline int C(int n, int m) {return (n < 0 || m < 0 || n < m) ? 0 : jc[n] * jcinv[n - m] % mod * jcinv[m] % mod;}
inline int S(int n, int m) {return C(m + n - 1, n - 1);}
signed main(){
	init();
	cin >> n;
	for(int i = 1; i <= n; ++i){
		scanf("%lld", &a), now2.clear();
		if(i == 1) {
			f[a][1][1] = 1, now.insert(a);
			continue;
		}
		for(auto j : now){
			int cnt = a - j + 2, sum = a - j + 1;
			if(sum < 0) continue;
			
			if(f[j][0][0])
				ADD(f2[cnt][0][0], f[j][0][0] * S(j - 1, sum)), now2.insert(cnt);
			
			if(f[j][0][1]){
				ADD(f2[cnt][0][0], f[j][0][1] * S(j - 1, sum)), now2.insert(cnt);
				ADD(f2[cnt - 1][0][1], f[j][0][1] * S(j, sum - 1)), now2.insert(cnt - 1);
			}
			
			if(f[j][1][0]){
				ADD(f2[cnt][0][0], f[j][1][0] * S(j - 1, sum)), now2.insert(cnt);
				ADD(f2[cnt - 1][1][0], f[j][1][0] * S(j, sum - 1)), now2.insert(cnt - 1);
			}
			
			if(f[j][1][1]){
				ADD(f2[cnt][0][0], f[j][1][1] * S(j - 1, sum)), now2.insert(cnt);
				ADD(f2[cnt - 1][0][1], f[j][1][1] * S(j, sum - 1)), now2.insert(cnt - 1);
				ADD(f2[cnt - 1][1][0], f[j][1][1] * S(j, sum - 1)), now2.insert(cnt - 1);
				ADD(f2[cnt - 2][1][1], f[j][1][1] * S(j + 1, sum - 2)), now2.insert(cnt - 2);
			}
		}
		for(auto j : now)
			f[j][0][0] = f[j][0][1] = f[j][1][0] = f[j][1][1] = 0;
		for(auto j : now2)
			for(int q = 0; q < 2; ++q)
				for(int p = 0; p < 2; ++p)
					f[j][q][p] = f2[j][q][p], f2[j][q][p] = 0;
		now = now2;
	} 
	cout << (f[1][0][0] + f[1][0][1] + f[1][1][0] + f[1][1][1]) % mod;
	return 0;
}
posted @ 2023-11-13 19:49  Zwi  阅读(6)  评论(0编辑  收藏  举报