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;
}
posted @ 2024-01-16 13:42  fengxue-K  阅读(39)  评论(0)    收藏  举报