CF-675-E-RMQ优化DP
675-E 题目大意
有\(n\)个车站,以及一个长度为\(n\)的序列\(a_1,a_2······a_n\),第\(i\)个车站可以直接到达\([i+1,a_i]\)中的任意一个车站。记\(p[i][j]\)为从车站\(i\)到车站\(j\)的最小步数,求:
\[\sum_{i=1}^{n}\sum_{j=i+1}^{n}p[i][j]
\]
Solution
记\(dp[i]\)为从车站\(i\)到后面车站的最小步数和,正着考虑一个车站可能对应着多个后面的车站的状态,不妨倒着考虑,车站\(i\)的状态能够从后面哪个车站转移而来。
基于贪心,我们应当从区间\([i+1,a_i]\)中选出一个下一步能够走得最远的车站\(j\),即\(max(a[j]),j∈[i+1,a_i]\)。这时转移方程为:
\[dp[i]=dp[j]+j-i+(n-a[i])
\]
这里转移是怎么来的,分两部分考虑,一部分在\([i+1,a_i]\)中,这里面的车站\(i\)可以一步到达,这里的贡献为\(j-i+(dp[j]\)的一部分\()\),另一部分则是在\([a_i+1,n]\)中,这里面的车站需要\(i\)先转移到\(j\)再转移过去,换句话说就是在车站\(j\)到这些车站最小步数的基础上还需要多走一步,这一部分的贡献为\(n-a[i]+(dp[j]\)的另一部分\()\)。
转移方程找到之后,剩下的问题就是在一段区间\([l,r]\)中找到最大值\(a[j]\),这部分就是\(RMQ\)问题了,用\(ST\)表、树状数组、线段树均可,时间复杂度\(O(nlogn)\)。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template<typename T>
struct Fenwick{
int n;
vector<T> tr;
Fenwick(int _n):tr(_n+1),n(_n){
for(int i=0;i<=n;i++) tr[i]={0,0};
}
int lowbit(int x){
return x&-x;
}
void add(int u,T x){
for(int i=u;i<=n;i+=lowbit(i)) if(x.first>=tr[i].first) tr[i]=x;
}
T query(int u){
T res={0,0};
for(int i=u;i;i-=lowbit(i)) if(tr[i].first>=res.first) res=tr[i];
return res;
}
};
int main(){
int n;
cin>>n;
vector<int> a(n+1);
for(int i=1;i<n;i++){
cin>>a[i];
a[n]=n;
}
vector<vector<pair<int,int>>> e(n);
ll ans=0;
Fenwick<pair<int,int>> fen(n*2);
fen.add(n,{n,n});
vector<ll> dp(n+1);
for(int i=n-1;i;i--){
auto p=fen.query(a[i]);
dp[i]=dp[p.second]+p.second-i+n-a[i];
ans+=dp[i];
fen.add(i,{a[i],i});
}
cout<<ans<<'\n';
return 0;
}

浙公网安备 33010602011771号