AtCoder Beginner Contest 372 F Solution

题意

给出一个按标号排序的环,以及 \(m\) 条辅助边,求从节点 \(1\) 开始走 \(k\) 步的方案数。\(2\le n\le 2\times 10^5\)\(1\le m\le {\color{red}{50}}\)\(1\le k\le 2\times 10^5\)

思路

本题有显然的 \(O(k(n+m))\) 暴力转移思路,但数据范围决定了这是不可行的。考虑优化。

首先考虑 \(m=0\) 的情况,即没有辅助边。在这种情况下,不管走几步,高桥君能够抵达的点都是唯一的。

在这种情况下,DP 数组就像是每次向后循环位移了一位。这也正是本题的关键。

\(d_i\) 为走到节点 \(i\) 的方案数。

首先,为了处理方便,不妨将所有节点的标号减 \(1\),这样旋转操作可以转化为标号取模操作。

在旋转辅助边的情况下,我们可以忽略对原环上边的转移。边 \((x,y)\) 在第 \(t\) 次转移时的实际效果是

\[d_{(y-t)\bmod n}\to d_{(y-t)\bmod n}+d_{(x-t+1)\bmod n} \]

原因是,若数组整体向后偏移一位,相对的,在原数组上的位置相应向前偏移一位。由于转移前数组未偏移,所以得到额外方案的位置少偏移一位。

另外,由于所有操作需要同时进行,所以需要缓存值后再操作。

时间复杂度 \(O(n+mk)\)

代码

#include <cstdio>
#include <utility>
using namespace std;
const int N = 2e5 + 10, K = 5e1 + 10, mod = 998244353;
int n, m, k, x[N], y[N], dp[N], res;
using pii = pair<int, int>;
pii ad[K];
#define adm(x, y, mod) ((x) + (y) >= (mod) ? (x) + (y) - (mod) : (x) + (y))
int main()
{
    scanf("%d%d%d", &n, &m, &k);
    dp[0] = 1;
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d", x + i, y + i);
        x[i]--, y[i]--;
    }
    for (int i = 0, p = 1, p2 = 0; i < k; i++, p = adm(p, 1, n), p2 = adm(p2, 1, n))
    {
        for (int j = 1; j <= m; j++)
        {
            ad[j] = {adm(y[j], n - p, n), dp[adm(x[j], n - p2, n)]};
        }
        for (int j = 1; j <= m; j++)
        {
            dp[ad[j].first] = adm(dp[ad[j].first], ad[j].second, mod);
        }
    }
    for (int i = 0; i < n; i++)
        res = adm(res, dp[i], mod);
    printf("%d\n", res);
}
posted @ 2024-09-22 08:57  丝羽绫华  阅读(51)  评论(0编辑  收藏  举报