Codeforces Round 965 D Solution

闲话

在题解区,故意将解法复杂化,在明显可以以更低时间复杂度解决且代码更容易理解的题目中提交时间复杂度更高的题解而凸显自己看起来更高的水平是一股客观存在的不良风气,就如同这题一样。

本题难度实际上低于 C。

题解

线性 DP。

首先考虑 Elsie 淘汰 Bessie 的充要条件。由于 Bessie 无法使用辅助边,所以为了淘汰 Bessie,Elsie 显然需要借助辅助边先于 Bessie 到达某个点。此时,我们可以计算 Elsie 在只使用 \(s\) 位置前的边时 Elsie 走到每个位置的最小用时。

以下定义 \(d_i\) 为 Elsie 走到每个位置的最小用时。初始时,定义 \(d_i=i\)

首先,本题使用的从低编号到高编号的单向边是使用线性 DP 解决本题的重要前提,因为它保证了高编号的 \(d_i\) 一定会通过低编号的 \(d_i\) 唯一确定。由于题目需要对每个除了 \(n\) 之外的 \(s\) 都求出答案,考虑如何分步进行 DP。

对于位置 \(i\),容易注意到位置 \(i\) 前的所有辅助边都可以使用,而位置 \(i\) 后的辅助边并没有使用的必要。为什么?由于 Bessie 从位置 \(i\) 开始,所以若 Elsie 走到了 \(i\) 之后的一个位置,只有两种可能:Elsie 超过了 Bessie,或这个位置已经坍塌。

也就是说,当 \(s\)\(i\) 变为 \(i+1\) 时,我们可以额外激活从位置 \(i\) 开始的所有辅助边,更新这之后的 \(d_j\)

现在考虑如何判断 Bessie 是否会赢。我们将为 Elsie 设计的距离计算方法搬到 Bessie 这里,定义 \(c_i\) 为 Bessie 走到每个位置的最小用时。显然,对于 Bessie 来说,\(c_i=i-s+1\)。由于 Bessie 先手,所以若 Elsie 和 Bessie 在同一个时间点到达同一个位置,那么 Bessie 会离开这个位置,使 Elsie 被淘汰。实际上,对于确定的 \(d_i\),我们可以方便地确定 Bessie 安全到达这个位置所需要的最小 \(s\) 值,我们定义其为 \(p_i\)。由于 Bessie 速度固定为 \(1\),那么 \(p_i=i-d_i+1\)。同时,由于我们可以确定每条边的位置,所以我们可以得到全局最小 \(s\) 值,定义其为 \(r\),则 \(r=\max_{i=1}^n p_i\)

显然,若 \(s\ge r\),那么 Bessie 必胜,否则 Elsie 必胜。

由于我们需要 \(n\) 次计算初始化所有的 \(d_i\),需要 \(m\) 次计算使用每条边更新 \(d_i\),所以时间复杂度为 \(O(n+m)\)

代码

在以下的代码中,mnp 指代 \(r-1\),因为更方便维护。

#include <iostream>
#include <vector>
using namespace std;
const int N = 2e5 + 10;
int n, m, dp[N], mnp;
vector<int> road[N];
bool res[N];
void run()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        road[i].clear();
        dp[i] = i;
    }
    for (int i = 1, x, y; i <= m; i++)
    {
        scanf("%d%d", &x, &y);
        road[x].emplace_back(y);
    }
    mnp = 0;
    res[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        dp[i - 1] = min(dp[i - 1], dp[i - 2] + 1);
        for (auto &j : road[i - 1])
        {
            dp[j] = min(dp[j], dp[i - 1] + 1);
            mnp = max(mnp, j - dp[j]);
        }
        res[i] = (i > mnp);
    }
    for (int i = 1; i < n; i++)
    {
        printf("%d", res[i]);
    }
    putchar('\n');
}
int main()
{
    int T = 1;
    scanf("%d", &T);
    while (T--)
        run();
}
posted @ 2024-08-30 08:43  丝羽绫华  阅读(12)  评论(0)    收藏  举报