[线段树][二分][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

 

posted @ 2020-10-21 20:26  Vagari  阅读(115)  评论(0编辑  收藏  举报