分析:这个题刚看起来无从下手
但是我们可以先简化问题,首先可以固定起点i,求出i+1到n的最小距离
它可以到达的范围是[i+1,a[i]],贪心的想,我们希望换一次车可以到达的距离尽量远
即:找一个k,使得i+1<=k<=a[i],a[k]的值最大,就可以保证,换一次车,可以到达的距离最
找k的操作可以用线段树来完成
统计当前dp[i]=dp[k]+(n-i)-(a[i]-k),因为当前区间内的点在[k+1,a[i]]的点多计了一次,所以减去
#include <stdio.h> #include <iostream> #include <algorithm> #include <string.h> #include <stdlib.h> #include <map> #include <queue> #include <set> using namespace std; typedef long long LL; const int INF=0x3f3f3f3f; const int N=1e5+5; int c[N<<2],a[N]; LL dp[N]; void up(int rt){ if(a[c[rt<<1]]>=a[c[rt<<1|1]]) c[rt]=c[rt<<1]; else c[rt]=c[rt<<1|1]; } void build(int rt,int l,int r){ if(l==r){c[rt]=l;return;} int m=(l+r)>>1; build(rt<<1,l,m); build(rt<<1|1,m+1,r); up(rt); } int ask(int rt,int l,int r,int x,int y){ if(x<=l&&r<=y)return c[rt]; int ls=-1,rs=-1,m=(l+r)>>1; if(x<=m)ls=ask(rt<<1,l,m,x,y); if(y>m)rs=ask(rt<<1|1,m+1,r,x,y); if(ls==-1)return rs; if(rs==-1)return ls; if(a[ls]>=a[rs])return ls; else return rs; } int main(){ int n; scanf("%d",&n); for(int i=1;i<n;++i) scanf("%d",&a[i]); a[n]=n; LL ret=0; build(1,1,n); for(int i=n-1;i>0;--i){ int pos=ask(1,1,n,i+1,a[i]); dp[i]=dp[pos]+(n-i)-(a[i]-pos); ret+=dp[i]; } printf("%I64d ",ret); return 0; }