[每日 C] Kevin and Puzzle
前言
考试的时候, 一定是确定策略 \(\to\) 确定时间 \(\to\) 做题, 做题的时候心态要好
平时和这个也类似, 只是不用确定策略, 还需要听讲和检验
思路
看着像 \(\rm{dp}\) , \(\textrm{CSP-S 2024 T3}\) , 请
转化题意
找到一组 诚实 / 说谎 的分配, 使得没有两个说谎者可以站在一起, 并且满足所有诚实的人说的都是真的
考虑令 \(f_{i, 0/1}\) 表示考虑到 \(i\) 人, 其中这个人是 诚实 还是 说谎
考虑转移, 可以确定的是不能记录当前有多少个说谎者, 考虑找性质使其可以直接解决
如果当前人说谎
那么左边那个人必定是实话
否则当前人诚实
\(a_i = a_{i - 1}\)
如果当前人诚实, 分类讨论左边那个人
- 左边那个人诚实
这种情况下一定满足 - 左边那个人说谎
这种情况下, 左边的左边一定是实话, 所以我们需要知道 \(a_{i - 2} + 1 = a_i\) , 这种情况才可能成立
\(a_i \neq a_{i - 1}\)
如果当前人诚实, 分类讨论左边那个人
-
左边那个人诚实
这种情况下一定不满足 -
左边那个人说谎
这种情况下, 左边的左边一定是实话, 所以我们需要知道 \(a_{i - 2} + 1 = a_i\) , 这种情况才可能成立
发现这样分讨是推出来之后的理解, 还是换种分讨方式更好理解 (\(\textrm{orz klr}\))
当前假
左边一定真, 不需要讨论
当前真
左边假
左边的左边一定真, \(a_i = a_{i - 2}\)
左边真
显然 \(a_i = a_{i - 1}\)
实现
#include <bits/stdc++.h>
const int MAXN = 2e5 + 20;
const int MOD = 998244353;
namespace calc {
int add(int a, int b) { return a + b > MOD ? a + b - MOD : a + b; }
int sub(int a, int b) { return a - b < 0 ? a - b + MOD : a - b; }
int mul(int a, int b) { return (a * b * 1ll) % MOD; }
void addon(int &a, int b) { a = add(a, b); }
void mulon(int &a, int b) { a = mul(a, b); }
} using namespace calc;
int n;
int a[MAXN];
int dp[MAXN][2];
int main()
{
int t; scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
memset(dp, 0, sizeof dp);
dp[0][0] = 1;
for (int i = 1; i <= n; i++) {
addon(dp[i][1], dp[i - 1][0]);
if (a[i] == a[i - 1]) {
if (a[i - 2] + 1 == a[i]) addon(dp[i][0], dp[i - 1][1]);
addon(dp[i][0], dp[i - 1][0]);
} else {
if (a[i - 2] + 1 == a[i]) addon(dp[i][0], dp[i - 1][1]);
}
}
printf("%d\n", add(dp[n][0], dp[n][1]));
}
return 0;
}
总结
善于通过题给条件进行分类讨论, 从而减小复杂度
思考题目给出这个条件的意义是什么