停不下来的团长奥尔加

2020.11.2

题目描述

奥尔加在一个长度为 \(n+1\) 的街道上跑着,初始,奥尔加在位置 \(1\) 上,他想要跑到位置 \(n+1\) 去保护团员 ride on。空旷的大路上难免会有暗杀者,当奥尔加走到位置 \(i\) 时,会有暗杀者出现,这时奥尔加只能回到 \(p_i \ (1\leq p_i\leq i)\) 位置上,但在下次到达 \(i\) 的时候,暗杀者就不会出现(如果再到达 \(i\) 还会有暗杀者),换言之,当奥尔加此时是第奇数次到达 \(i\) 的时候,下一步会走到 \(p_i\),第偶数次到达 \(i\) 的时候,下一步走到 \(i+1\)\(n+1\) 位置上没有暗杀者)。奥尔加每次移动都需要花费 \(1\) 单位时间,停不下来的奥尔加只会一直跑,他想知道自己什么时候才能到达 \(n+1\) 位置,因为答案可能会很大,你只需要告诉他答案对 \(1000000007\ (10^9+7)\) 取模的结果就好了。

解法

一开始想的分层图,什么玩意儿啊……后来想dp,最后三分钟把式子推出来了……没时间写

\(dp_i\) 表示第一次走到 \(i\) 的时候是第几步,那么答案就是 \(dp_{n+1}\)
\(f_i\) 表示当前在位置 \(i\),跳到 \(p_i\) 后再次到达位置 \(i\) 需要的步数。

那么

\[\begin{cases} dp_1=0 \\ f_1=1 \\ dp_n=dp_{n-1}+f_{i-1}+1 \\ f_n=(\sum_{i=a_i}^{n-1} f_i+1)+1 \end{cases}\]

\(f_n\) 的转移前缀做差即可。

#include<stdio.h>
#define ll long long
#define N 1000007
#define Mod 1000000007

inline int read(){
    int x=0; bool flag=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
    return flag? x:-x;
}

ll dp,f[N],s[N];
int n,a[N];
int main(){
    freopen("rideon.in","r",stdin);
    freopen("rideon.out","w",stdout);
    n=read(),a[1]=read();
    dp=0,f[1]=s[1]=1;
    for(int i=2;i<=n;i++){
        int x=read();
        dp=(dp+f[i-1]+1)%Mod;
        f[i]=((s[i-1]-s[x-1]+i-x+1)%Mod+Mod)%Mod;
        s[i]=(s[i-1]+f[i])%Mod;
    }
    printf("%lld",dp+f[n]+1);
}
posted @ 2020-11-03 09:01  Kreap  阅读(184)  评论(1编辑  收藏  举报