ARC074E

\(f_{i, j, k}\) 表示从 \(i\) 往前,第一个与 \(a_i\) 颜色不同的位置是 \(j\),第一个颜色与 \(a_i, a_j\) 都不相同的位置是 \(k\) 的方案数,其中某个值为 \(0\) 表示这个位置不存在。当然 \(i\gt j\gt k\)(特别地,当 \(j=0\)\(k\) 可以为 \(0\)

如果我们有一个形如 \((l,r,x)\) 的限制,那么这样的三元组 \((i,j,k)(r=i)\) 是不符合限制的(即 \(f_{i,j,k}\) 需要设为 \(0\)):

  • \(x=1\)\(l \le j\),因为 \([l,r]\) 这段区间内出现了一个与 \(a_i\) 不同的 \(a_j\)

  • \(x=2\)\(l \le k\)\(j\lt l\),前者是因为出现了第三种既不同于 \(a_i\) 也不同于 \(a_j\) 的颜色,后者是因为距离 \(i\) 最近的不同于 \(a_i\)\(a_j\) 并不在 \([l,r]\) 内,即 \([l,r]\) 这段区间内只有一个颜色。

  • \(x=3\)\(k\lt l\),因为距离 \(i\) 最近的第三种不同的颜色 \(a_k\)\([l,r]\) 之外,即 \([l,r]\) 内至多只有两种颜色。

所以用 \(n\)vector 来保存限制,每次到一个位置把以它为右端点的限制取出然后剔除不合法的状态。

考虑从 \(i\) 转移 到 \(i+1\)

  • \(a_{i+1}=a_i\),则 \(f_{i+1,j,k}\gets f_{i+1,j,k}+f_{i,j,k}\)
  • \(a_{i+1}=a_j\),则 \(f_{i+1,i,k}\gets f_{i+1,i,k}+f_{i,j,k}\)
  • \(a_{i+1}=a_k\),则 \(f_{i+1,i,j}\gets f_{i+1,i,j}+f_{i,j,k}\)

初值为 \(f_{1,0,0}=3\),因为这里的 DP 只考虑颜色相对之间的关系,而 \(a_1\) 有三种选法,确定了 \(a_1\) 之后,所有合法的方案都被不重不漏的划分了。

最终答案就是所有 \(f_{n,j,k}\) 的和,其中 \(j\lt n,k\lt\max(1,j)\)

时间复杂度 \(\mathcal O(n^3)\)

Code:

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef pair <int, int> pii;
typedef long long ll;
const int N = 305, mod = 1e9 + 7;
int n, m;
vector <pii> vec[N];
int f[N][N][N];

void add(int &a, int b) {
	a += b;
	if (a >= mod) a -= mod;
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1, l, r, cnt; i <= m; ++i) scanf("%d%d%d", &l, &r, &cnt), vec[r].emplace_back(l, cnt);
	f[1][0][0] = 3;
	for (int i = 0; i <= n; ++i) {
		for (auto it : vec[i]) {
			int l = it.fi, cnt = it.se;
			for (int j = 0; j < i; ++j) {
				int lim = j ? j - 1 : 0;
				for (int k = 0; k <= lim; ++k) {
					if (cnt == 1 && l <= j) f[i][j][k] = 0;
					if (cnt == 2 && (l <= k || j < l)) f[i][j][k] = 0;
					if (cnt == 3 && k < l) f[i][j][k] = 0;
				}
			}
		}
		if (i == n) break;
		for (int j = 0; j < i; ++j) {
			int lim = j ? j - 1 : 0;
			for (int k = 0; k <= lim; ++k) {
				if (!f[i][j][k]) continue;
				add(f[i + 1][j][k], f[i][j][k]), add(f[i + 1][i][k], f[i][j][k]), add(f[i + 1][i][j], f[i][j][k]);
			}
		}
	}
	int ans = 0;
	for (int j = 0; j < n; ++j) {
		int lim = j ? j - 1 : 0;
		for (int k = 0; k <= lim; ++k)
			add(ans, f[n][j][k]);
	}
	printf("%d", ans);
	return 0;
}
posted @ 2022-11-06 16:36  Kobe303  阅读(10)  评论(0编辑  收藏  举报