CF407B Long Path

好玩的题。

首先我们(看一下题解之后)发现当你第一次走到了一个点的时候,那么它之前的所有点一定都访问过了偶数次。

假设我们第一次走到了一个点$i$,那么$i - 1$一定访问了偶数次,那么第一次走$i - 1$的时候会走到$p_{i - 1}$,也就是说不断重复这个走偶数次的过程最终到达了$i$。

因为题目保证了$p_i leq i$,所以我们可以$dp$了,设$f_{i, j}$表示从$i$走到$j$需要的步数。

边界:$f_{i, i} = 1$。注意这里是从$i$走到$i$,而不是停在原地不动了。

有方程:$f_{i, j} = f_{i, j - 1} + f_{p_{j - 1}, j - 1} + 1$。

解释一下:要从$i$走到$j$,需要先走到$j - 1$,然后从$p_{j - 1}$走回到$j - 1$,再走一步走到$j$。

最后答案是$f_{1, n + 1} - 1$,因为一开始就在$1$。

然而转移顺序似乎并不明显,记忆化搜索实现。

时间复杂度$O(n^2)$。

Code:

#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1005;
const int P = 1e9 + 7;

int n, nxt[N], f[N][N];

inline void read(int &X) {
    X = 0; char ch = 0; int op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

inline void inc(int &x, int y) {
    x += y;
    if(x >= P) x -= P;
}

inline void sub(int &x, int y) {
    x -= y;
    if(x < 0) x += P;
}

int dfs(int i, int j) {
    if(i == j) return f[i][j] = 1;
    if(f[i][j]) return f[i][j];
    int res = 0;
    inc(res, dfs(i, j - 1));
    inc(res, dfs(nxt[j - 1], j - 1));
    inc(res, 1);
    return f[i][j] = res;
}

int main() {
    read(n);
    for(int i = 1; i <= n; i++) read(nxt[i]);
    int ans = dfs(1, n + 1);
    sub(ans, 1);
    printf("%d
", ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/CzxingcHen/p/9890193.html