CF675E Trains and Statistic
Solution
这是DP。
对于每一个位置 \(i\) ,它所能到的范围是 \([i+1,a_i]\) ,所以如果想要走的步数最少并且最远,应该找 \([i+1,a_i]\) 中有 \(\max\{a_{k}\}\) 的 \(k\) 。
这个可以用RMQ解决。
那么对于 \(i\) ,有了相应的 \(k\) ,接下来该怎么转移呢?
\(k\) 是在 \(i\) 后面的,所以考虑从后往前转移。
设 \(f_{i}=\sum\limits_{j=i+1}^np_{i,j}\) ,那么转移方程是:
\[f_{i}=f_k+(n-k)-(a_i-k)+(k-i)\rightarrow f_i=f_k+(n-i)-(a_i-k)
\]
其中 \(n-k\) 是因为 \(>k\) 的点均经过 \(k\) , \(a_i-k\) 是因为这一部分被算了两次贡献,所以减去, \(k-i\) 是 \([i+1,k]\) 这一部分的点的贡献。
Code
我就是不会二分和单调栈,只能用st表的那个屑≧ ﹏ ≦
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N=100010;
int n,ans,a[N],st[N][20],f[N];
inline int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
return x*f;
}
inline int Max(int x,int y){
return a[x]>a[y]?x:y;
}
inline void init(){
for(int j=1;j<=20;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
st[i][j]=Max(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
inline int ask(int l,int r){
int k=log2(r-l+1);
return Max(st[l][k],st[r-(1<<k)+1][k]);
}
signed main(){
n=read();
for(int i=1;i<n;i++){
a[i]=read();
st[i][0]=i;
}
init();
for(int i=n-1;i>=1;i--){
int k=ask(i+1,a[i]);
if(a[i]>=n) f[i]=n-i;
else f[i]=f[k]+(n-i)-(a[i]-k);
ans+=f[i];
}
printf("%lld\n",ans);
return 0;
}