LG9691

考虑 dp。设 \(f_i\) 表示到位置 \(i\) 所需的最小花费,且第 \(i\) 个位置必选,现在要找上一个决策点 \(j\),这个点应该要在此前所有区间的左端点的后面,才能保证这些区间能被覆盖(即确保至少一个在之前每个区间内),则 \(j\) 应满足 \(\max_{r_k<i}l_k \le j < i\),转移方程 \(f_i=\min_{{\max_{r_k<i}l_k} \le j < i}f_j+a_i\)。左边部分将区间按右端点排序后双指针即可,转移使用单调队列即可。

实现时可以令 \(a_{n+1}=0\),这样 \(f_{n+1}\) 可以直接作为答案输出。时间复杂度 \(O(\sum(n+m\log m))\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 1000010
#define int long long
using namespace std;
int n,m,L,R,a[N],q[N],f[N];
struct line{
    int l,r;
}c[N];
bool cmp(line x,line y){
    return x.r<y.r;
}
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i],f[i]=0;    
    a[n+1]=0;
    cin>>m;
    for(int i=1;i<=m;i++)
        cin>>c[i].l>>c[i].r;
    sort(c+1,c+m+1,cmp);
    int j=1;
    int mx=0;
    f[1]=a[1];
    L=1,R=2;
    q[L]=0,q[R]=1;
    for(int i=2;i<=n+1;i++){
        while(j<=m&&c[j].r<i)
            mx=max(mx,c[j].l),j++;
        while(L<=R&&q[L]<mx)L++;
        f[i]=f[q[L]]+a[i];
        while(L<=R&&f[q[R]]>=f[i])R--;
        q[++R]=i;
        // cout<<q[L]<<' '<<i<<' '<<f[i]<<endl;
    }
    cout<<f[n+1]<<'\n';
    return;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int T;
    cin>>T;
    while(T--)
        solve();
    return 0;
}
posted @ 2025-09-12 08:17  FormulaOne  阅读(9)  评论(0)    收藏  举报