「2021-09-12 联考」I love random (C)

考虑DP,记\(f(i,j)\)表示第\(i\)个数最终是\(j\)的方案数。

那么考虑这个点的转移,它可能被前后的数覆盖,也可能覆盖前后的数。

为了方便,不妨只考虑每个点往前覆盖(或不动),以及被前面覆盖的情况。

首先考虑往前覆盖,那么它最多覆盖到单调栈中上一个元素,记那么位置为\(p\),那么有转移,\(f(i,A_i)\gets\sum_{j=p}^{i-1}\sum_{k=1}^Nf(j,k)\),同时更新一下这些点最终为\(A_i\)是的dp值。

接着考虑它被覆盖的情况,它一定只能被单调栈中的元素覆盖。那么考虑它前面的一个位置,若\(i\)最终为\(j\),那么前一个数一定只能填\(\leq j\)的数字,转移一下即可。

因为这题还卡空间,稍微优化一下可以做到时间复杂度\(\mathcal O(N^2)\),空间复杂度\(\mathcal O(N)\)

Code
#include <bits/stdc++.h>

int main(void) {
	freopen("C.in", "r", stdin), freopen("C.out", "w", stdout);
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout.tie(nullptr);
	
	const int P = 1E9 + 7;
	auto incr = [&](int& a, int b) {
		a -= (a += b) >= P ? P : 0;
	};
	
	int N;
	std::cin >> N;
	std::vector <int> A(N + 1, 0);
	for (int i = 1; i <= N; ++i)
		std::cin >> A[i];
	
	std::vector <int> dp(N + 1, 0), eva(N + 1, 0), _(N + 1, 0), Q(N, 0), __(N + 1, 0);
	int top = 0;
	
	Q[top] = dp[0] = dp[1] = 1;
	for (int i = A[1]; i <= N; ++i)
		eva[i] = 1;
	for (int i = 2; i <= N; ++i) {
		_[A[i]] = dp[i - 1];
		while ((~top) && A[Q[top]] > A[i])
			--top;
		Q[++top] = i;
		
		for (int j = 0; j < top; ++j)
			_[A[Q[j]]] = eva[A[Q[j]]];
		eva = _, _ = __;
		
		int k = 0;
		for (int j = i - 1; j; --j) {
			if (A[j] < A[i]) {
				k = j;
				break;
			}
			incr(eva[A[i]], dp[j - 1]);
		}
		
		for (int j = k + 1; j < i; ++j) {
			incr(dp[j], dp[j - 1]);
		}
		
		for (int j = 1; j <= N; ++j)
			incr(eva[j], eva[j - 1]);
		dp[i] = eva[N];
	}
	
	std::cout << dp[N] << '\n';
	return 0;
}
posted @ 2021-10-30 16:06  Beginner2670  阅读(16)  评论(0)    收藏  举报