题目大意
Farmer John 给了他的 cows 一个程序玩。这个程序有两个整形变量 x 和 y,并且在一个正数序列 a1 a2 a3,...,an (2≤n≤2*105,1≤ai≤109)中执行以下操作:
1、初始化 x=1, y=0,如果经过任何步骤之后 x≤0 或者 x>n,程序立刻停止运行
2、x 和 y 同时增加 ax(注意:增加的值是 a 序列中的第 x 个,也就是 ax)
3、x 减小 ax,y 增加 ax
4、程序重复执行步骤 2 和步骤 3 直至终止
现在给了你 a2 a3 a4,...,an,程序总共运行了 n-1 次,第 i(1≤i≤n-1) 次运行的时候 a1=i,问程序停止运行之后 y 是多少?如果不能停止运行,输出 -1
做法分析
观察步骤 2 和步骤 3,可以知道:
1、y 是记录的 x 变化的值,程序终止只和 x 有关,与 y 无关
2、步骤 2 使得 x 增加,步骤 3 使得 x 减小
3、当 x 为 1 的时候,下一步必然是执行步骤 2(因为上面的第 2 条规律),永远不可能停止,输出 -1
4、当 x 表示的值重复出现并且下一步执行的步骤相同时,程序必然陷入一个循环中,永远不可能停止,输出 -1
想到了上面的四个规律,这道题目就简单了
定义 f(i, j) 表示:当 x=i 的时候,到程序终止,y 会加上的值是多少,那么,状态就是这样转移的:
f(i, 0)=f(i+ai, 1)+ai:这一步该执行步骤 2
f(i, 1)=f(i-ai, 0)+ai:这一步该执行步骤 3
在计算的过程中,如果 i+ai 或者 i-ai 超过范围,必然是程序终止了;如果 i=1 或者 i 是之前某个经过的节点,必然陷入死循环
那么每次 a1 变化的时候,直接输出 a1+f(1+a1, 1) 就行了
参考代码
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 5 using namespace std; 6 7 typedef long long LL; 8 const int N=200006; 9 const LL INF=((1LL)<<60); 10 11 LL f[N][2]; 12 bool vs[N][2]; 13 int n, A[N]; 14 15 LL DP(int x, int dir) 16 { 17 if(x>n || x<=0) return 0; 18 if(x==1) return INF; 19 if(f[x][dir]!=-1) return f[x][dir]; 20 if(vs[x][dir]) return INF; 21 vs[x][dir]=1; 22 LL res; 23 if(dir==0) res=DP(x+A[x], 1); 24 else res=DP(x-A[x], 0); 25 if(res!=INF) f[x][dir]=(LL)A[x]+res; 26 else f[x][dir]=INF; 27 return f[x][dir]; 28 } 29 30 int main() 31 { 32 scanf("%d", &n); 33 for(int i=2; i<=n; i++) scanf("%d", &A[i]); 34 memset(f, -1, sizeof f); 35 memset(vs, 0, sizeof vs); 36 for(int i=2; i<=n; i++) 37 { 38 if(f[i][0]==-1) f[i][0]=DP(i, 0); 39 if(f[i][1]==-1) f[i][1]=DP(i, 1); 40 } 41 for(int i=1; i<n; i++) 42 { 43 if(f[1+i][1]==INF) printf("-1\n"); 44 else printf("%I64d\n", i+f[1+i][1]); 45 } 46 return 0; 47 }