CF675E Trains and Statistic
阿新 • • 發佈:2020-12-03
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; }