• [线段树][二分][DP]luogu P1295 [TJOI2011]书架


    题面

    https://www.luogu.com.cn/problem/P1295

    分析

    很容易想到设 $f_i$ 表示选到第 $i$ 个的最小最大值之和,则有

    $f_i=min(f_j+max(hj~hi) (j<i)$

    发现这里有一段连续的东西,然后考虑线段树。

    考虑插入一个 $h_i$ 的影响,就会使从 $i$ 向前找到的第一个 $j(h[j-1]>=h[i])$ 的 $[i,j]$ 区间 $h$ 值全部设为 $h_i$

    那么开三棵线段树,一个维护 $f$ ,一个维护 $h$ ,一个维护 $max(h_j~h_i)$

    查找 $h$ 最值时二分即可

    代码

    #include <iostream>
    #include <cstdio>
    #define lson (x<<1)
    #define rson ((x<<1)+1)
    using namespace std;
    typedef long long L;
    const L Inf=1ll<<62;
    const int N=1e5+10;
    int n,m,h[N],rev[N];
    L t[4*N],lz[4*N],f[4*N],g[4*N];
    
    void Pushdown(int x) {if (!lz[x]) return;g[lson]=f[lson]+lz[x];g[rson]=f[rson]+lz[x];lz[lson]=lz[rson]=t[lson]=t[rson]=lz[x];lz[x]=0;}
    
    void Change(int x,int l,int r,int ll,int rr,int val) {
        if (r<l) return;
        if (ll<=l&&r<=rr) {g[x]=f[x]+val;t[x]=lz[x]=val;return;}
        int mid=l+r>>1;
        Pushdown(x);
        if (ll<=mid) Change(lson,l,mid,ll,rr,val);
        if (mid<rr) Change(rson,mid+1,r,ll,rr,val);
        t[x]=max(t[lson],t[rson]);f[x]=min(f[lson],f[rson]);g[x]=min(g[lson],g[rson]);
    }
    
    void Insert(int x,int l,int r,int k,int val) {
        if (l==r) {g[x]=val+t[x];f[x]=val;return;}
        int mid=l+r>>1;
        Pushdown(x);
        if (k<=mid) Insert(lson,l,mid,k,val);
        else Insert(rson,mid+1,r,k,val);
        f[x]=min(f[lson],f[rson]);g[x]=min(g[lson],g[rson]);
    }
    
    int Get(int x,int l,int r,int ll,int rr) {
        if (ll<=l&&r<=rr) return t[x];
        int mid=l+r>>1,ans=0;
        Pushdown(x);
        if (ll<=mid) ans=Get(lson,l,mid,ll,rr);
        if (mid<rr) ans=max(ans,Get(rson,mid+1,r,ll,rr));
        return ans;
    }
    
    int Query(int l,int r,int k) {
        int mid,ans=r+1,ub=r;
        while (l<=r) {
            mid=l+r>>1;
            if (Get(1,1,n,mid,ub)<k) ans=mid,r=mid-1;
            else l=mid+1;
        }
        return ans;
    }
    
    L Query(int x,int l,int r,int ll,int rr) {
        if (ll<=l&&r<=rr) return g[x];
        int mid=l+r>>1;
        L ans=Inf;
        Pushdown(x);
        if (ll<=mid) ans=Query(lson,l,mid,ll,rr);
        if (mid<rr) ans=min(ans,Query(rson,mid+1,r,ll,rr));
        return ans;
    }
    
    int main() {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) scanf("%d",&h[i]);
        Change(1,1,n,1,1,h[1]);
        for (int i=1,s=0,j=1;i<=n;i++) {
            s+=h[i];while (s>m) s-=h[j++];
            Change(1,1,n,Query(j,i-1,h[i]),i,h[i]);
            if (i==n) return printf("%lld",Query(1,1,n,j,i)),0;
            Change(1,1,n,i+1,i+1,h[i+1]);Insert(1,1,n,i+1,Query(1,1,n,j,i));
        }
    }
    View Code
  • 相关阅读:
    Android 开发学习进程0.19 webview 的使用
    2020年4到6月—7家公司面试总结(3家已拿offer)
    iOS今日头条第3轮面试回忆
    [搬运]Dart之枚举中使用扩展
    Proguard 常用规则
    shiro安全框架
    Android服务的AIDL跨进程(程序)操作
    Android——服务的实例,银行服务
    Android四大组件之服务————服务的生命周期和启动方式
    Android 程序间的广播和Manifest找不到(解决方法)
  • 原文地址:https://www.cnblogs.com/mastervan/p/13854477.html
Copyright © 2020-2023  润新知