停不下来的团长奥尔加
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);
}