我果然还是适合做这种简单数数,难的我根本不会做,哈哈!
首先我们发现,如果有两个完全相同的相邻的数,那么这两个数中的每一个数都不能够被删除,那么这实际上把整个序列分成了若干段,最后的答案就是每一段的答案的乘积。
现在考虑没有相邻相同的数。观察样例,可以发现形如 abab
的形式,中间的两个数不能够被同时删除。
更进一步,假如整个段形如 ababababab...
,那么发现被删除的数的限制为不能够相邻。在一段数中选取若干个不相邻的数,这个简单 DP 就能得出方案数,实际上是斐波那契数。
那么考虑不完全形如这样的数,假设段为 xababababy
,那么我们发现,中间这一段我们是可以删除一个前缀和一个后缀的。那么我们考虑将这一段划分成若干个 ababab
段,每相邻两端有一个公共的数。那么我们发现,假如每一段的两端的删除情况固定了,中间的删除情况是容易计算出方案数的。
那么我们就可以设一个简单的 DP,\(f_{i, 0/1}\) 表示考虑到第 \(i\) 段,这一段与上一段的交点是否被删除,直接 DP 即可。初始 \(f_{0,0}=1\),答案为 \(f_{m, 0}\),因为第一个数和最后一个数一定不能被删除。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005, P = 998244353;
int n, a[MAXN];
int f[MAXN];
int g[MAXN][2];
int c(int len, int a, int b) {
if (a == 0 && b == 0) {
return f[len - 2];
} else if (a == 1 && b == 1) {
int ans = 1 + len - 2;
for (int i = 0; i <= len - 4; i++) {
ans = (ans + 1ll * f[i] * (len - 3 - i)) % P;
}
return ans;
} else {
int ans = 1;
for (int i = 0; i <= len - 3; i++) {
ans = (ans + f[i]) % P;
}
return ans;
}
}
int solve(int l, int r) {
vector<int> t;
for (int i = l + 1, len = 2; i <= r; i++, len++) {
if (i == r || a[i - 1] != a[i + 1]) {
t.push_back(len);
len = 1;
}
}
int m = t.size();
g[0][0] = 1;
for (int i = 1; i <= m; i++) {
g[i][0] = g[i][1] = 0;
for (int a = 0; a <= 1; a++) {
for (int b = 0; b <= 1; b++) {
g[i][b] = (g[i][b] + 1ll * g[i - 1][a] * c(t[i - 1], a, b)) % P;
}
}
}
return g[m][0];
}
int main() {
scanf("%d", &n);
f[0] = 1, f[1] = 2;
for (int i = 2; i <= n; i++)
f[i] = (f[i - 1] + f[i - 2]) % P;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
int l = 1;
int ans = 1;
for (int i = 1; i <= n; i++) {
if (i == n || a[i] == a[i + 1]) {
ans = 1ll * ans * solve(l, i) % P;
l = i + 1;
}
}
printf("%d\n", ans);
return 0;
}
标签:ARC128D,删除,int,len,解题,MAXN,一段,Neq
From: https://www.cnblogs.com/apjifengc/p/17237436.html