Loading

【题解】ARC112E - Cigar Box

给定一个长度为 \(N\) 的排列 \(P\),问有多少种方式能通过恰好 \(M\) 次操作将排列还原成 \(1\sim N\),一次操作可以将一个数提到排列最前面或最后面。

显然对于一个数,我们只用保留对它的最后操作。因为如果我们去掉这个数,对其他数没有影响。而对这个数的操作会被最后一次操作覆盖掉。

直接枚举没有操作的数,这一定是初始序列 \(A\)​​ 中的连续一段上升序列。所以接下来我们可以直接 DP。定义状态 \(f_{i,l,r}\)​​ 表示进行前 \(i\)​​ 次操作,有前 \(l\)​​ 个数和后 \(r\)​​ 个数已经还原的方案数。

有转移 \(f_{i,j,k}\to f_{i+1,j+1,k},f_{i+1,j,k+1}\) 表示第 \(i+1\) 操作是某个数的最后一次操作,还有 \(f_{i,j,k}\times 2\times (l-j+r-k)\to f_{i+1,j,k}\)

这看起来就非常不优美,我们倒着做定义状态 \(f_{i,l,r}\) 表示进行最后的 \(i\) 次操作,有前 \(l\) 个数和后 \(r\) 个数已经还原的方案数,第一个转移不变,第二个转移变为 \(f_{i,j,k}\times 2\times (j+k)\to f_{i+1,j,k}\)

这样前面乘的系数之和 \(j+k\)​ 有关,所以我们可以合到一起 DP,令 \(x = j+k\)​,DP 后有 \(f_{i,l,r} = \binom{l+r}{l}f'_{i,l+r}\),这样就可以\(\mathcal{O}(NM)\) 计算。

#define N 3005
int n, m, a[N], f[N][N], c[N][N];

int main() {
	read(n), read(m);
	rp(i, n)read(a[i]);
	rep(i, 0, m){
		c[i][0] = 1;
		rp(j, i)c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % P;
	}
	f[0][0] = 1;
	rp(i, m)rp(j, n)f[i][j] = (f[i - 1][j - 1] + f[i - 1][j] * 2LL * j) % P;
	int ans = 0 ;
	rep(i, 1, n){
		rep(j, i, n){
			ad(ans, f[m][i - 1 + n - j] * 1LL * c[i - 1 + n - j][i - 1] % P);
			if(a[j] > a[j + 1])break;
		}
	}
	rep(i, 0, n)ad(ans, f[m][n] * 1LL * c[n][i] % P);
	cout << ans << endl;
	return 0;
}
posted @ 2022-01-01 18:28  7KByte  阅读(202)  评论(0编辑  收藏  举报