Loading

[每日 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;
}

总结

善于通过题给条件进行分类讨论, 从而减小复杂度

思考题目给出这个条件的意义是什么

posted @ 2025-01-21 10:31  Yorg  阅读(71)  评论(0编辑  收藏  举报