机房测试:停不下来的团长奥加尔(dp)

题目:

 

分析:

很明显是一个顺推的dp。

首先分析题目的性质:

如果现在向回走到了一个点,那么那个点一定是第奇数次被经过(离开它的时候一定是被偶数次经过)

定义dp [ i ]为到达i时是奇数次,再离开i,然后走到 i+1 所花费的时间。

答案就是dp[ n ]。

考虑怎么转移:

dp[ i ]=dp[ pi ] + dp[ pi+1 ]……dp[ i-1 ]。

就是先往回跳到pi,然后再花费dp[ pi ]的时间跳到pi+1,一直到 i点。

但是这样还不够,从i跳回pi花费了1的时间,回到 i 之后又花费了1的时间跳到 i+1,所以要在上式基础上+2.

最后维护一下前缀和就可以O(n)递推了。

#include<bits/stdc++.h>
using namespace std;
#define N 1000005
#define ll long long
#define ri register int
const int mod = 1e9+7;
int n,p[N],tim=0;
ll dp[N],sum[N];
/*
int cnt[N];
void dfs(int now)
{
    if(now==n+1) { printf("%d
",tim); exit(0); }
    cnt[now]^=1; tim=(tim+1) %mod;
    if(cnt[now]&1) dfs(p[now]);
    else dfs(now+1);
}*/
int read()
{
    int x=0,fl=1; char ch=getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') fl=-1; ch=getchar(); }
    while(ch<='9' && ch>='0') x=x*10+ch-'0',ch=getchar();
    return x*fl;
}
int main()
{
    freopen("rideon.in","r",stdin);
    freopen("rideon.out","w",stdout);
    scanf("%d",&n);
    for(ri i=1;i<=n;++i) p[i]=read();
    for(ri i=1;i<=n;++i){
        ll tot=0;
        if(i-1>=p[i]) tot=( (sum[i-1]-sum[p[i]-1]) %mod +mod) %mod;
        dp[i]=tot+2;
        sum[i]=(sum[i-1]+dp[i]) %mod;
    }
    printf("%lld
",sum[n] %mod);
    //dfs(1);
    return 0;
}
/*
2
1 2
5
1 1 1 1 1
2
1 1

3 
1 1 1

4
1 1 1 1

7 
1 1 2 2 3 2 4

8
1 2 1 1 3 2 2 4 

41
1 1 1 1 4 1 1 2 2 5 7 1 5 7 11 3 8 3 2 11 9 8 15 4 7 1 5 6 1 17 26 19 21 33 32 3
5 29 11 27 23 31
*/
View Code
原文地址:https://www.cnblogs.com/mowanying/p/11815541.html