1. 程式人生 > 實用技巧 >Luogu1848 [USACO12OPEN]Bookshelf G

Luogu1848 [USACO12OPEN]Bookshelf G

Description

link

Solution

\(f_i\) 表示前 \(i\) 本書放到書架上所得到的最小高度

轉移就挺顯然的,列舉哪一段是當前書架裡面的書就完了,單調佇列維護和小於 \(L\) 的性質

\[f_{i}=\min_{s_i-s_{j-1}\le L} {f_j+\max_{k=j+1}^ih_i} \]

這個方程的決策點每次必然右移,如上面所說,用單調佇列維護,彈隊頭就能做,但是還是 \(n^2\)

每次新增點的時候就是對於單調佇列裡面的所有值進行取 \(max\) 操作

彈隊尾,如果當前點的 \(f_i \le f_{q[tail]}+calc(q[tail]+1,i)\)

就彈

\(calc\)\(ST\) 表維護區間最值

然後發現彈隊尾就錯了

其實本質上面新加入一個點就是把單調佇列裡面的後面的部分進行取 \(max\)

然後這東西其實可以考慮用一棵線段樹維護,支援區間推平成定值,維護上 \(f,h\) 兩個量就可以了

降低複雜度的剪枝方法:維護區間 \(h\) 的最大最小,推平的時候按照大小關係走一發

Code

#include<bits/stdc++.h>
using namespace std;
#define reg register 
#define int long long
#define For(i,a,b) for(reg int i=a;i<=b;++i) 
namespace yspm{
    inline int read()
    {
        int res=0,f=1; char k;
        while(!isdigit(k=getchar())) if(k=='-') f=-1;
        while(isdigit(k)) res=res*10+k-'0',k=getchar();
        return res*f;
    }
    const int N=1e5+10;
    int h[N],w[N],s[N],n,l,f[N];
    deque<int> q;
    struct node{
        int sum,l,r,f,mx,mn,fl;
        #define l(p) t[p].l
        #define r(p) t[p].r
        #define f(p) t[p].f
        #define mx(p) t[p].mx
        #define mn(p) t[p].mn
        #define fl(p) t[p].fl
        #define sum(p) t[p].sum
    }t[N<<2];
   // #define min(a,b) (a<b?a:b)
   // #define max(a,b) (a>b?a:b)
    inline void push_up(int p)
    {
        mn(p)=min(mn(p<<1),mn(p<<1|1)); 
        mx(p)=max(mx(p<<1),mx(p<<1|1)); 
        sum(p)=min(sum(p<<1),sum(p<<1|1)); 
        f(p)=min(f(p<<1),f(p<<1|1));
        return ;
    }
    inline void push_down(int p)
    {
        if(!fl(p)) return ;
        mx(p<<1)=mx(p<<1|1)=mn(p<<1)=mn(p<<1|1)=fl(p);
        sum(p<<1|1)=f(p<<1|1)+fl(p);
        sum(p<<1)=f(p<<1)+fl(p);
        fl(p<<1)=fl(p<<1|1)=fl(p);
        fl(p)=0; 
        return ;
    }
    inline void build(int p,int l,int r)
    {
        l(p)=l; r(p)=r; if(l==r) return ;
        int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r);
        return ;
    }
    inline void update(int p,int l,int r,int v)
    {
        if(mn(p)>=v) return ;
        if(l<=l(p)&&r(p)<=r)
        {
            if(mx(p)<=v) return mx(p)=mn(p)=fl(p)=v,sum(p)=v+f(p),void();
        }int mid=(l(p)+r(p))>>1; push_down(p);
        if(l<=mid) update(p<<1,l,r,v); if(r>mid) update(p<<1|1,l,r,v);
        return push_up(p);
    }
    inline int query(int p,int l,int r)
    {
        if(l<=l(p)&&r(p)<=r) return sum(p);
        int mid=(l(p)+r(p))>>1,res=1e18+10; push_down(p);
        if(l<=mid) res=min(res,query(p<<1,l,r));
        if(r>mid) res=min(res,query(p<<1|1,l,r)); 
        return push_up(p),res;   
    }
    inline void insert(int p,int pos)
    {
        if(l(p)==r(p)) return f(p)=f[pos],void();
        int mid=(l(p)+r(p))>>1; push_down(p);
        if(pos<=mid) insert(p<<1,pos); else insert(p<<1|1,pos);
        return push_up(p);
    }
    signed main()
    {
        n=read(); l=read(); build(1,0,n);
        For(i,1,n) h[i]=read(),w[i]=read(),s[i]=s[i-1]+w[i];
        q.push_back(0);
        For(i,1,n) 
        {
            while(s[i]-s[q.front()]>l) q.pop_front(); q.push_back(i);
            update(1,q.front(),i-1,h[i]); f[i]=query(1,q.front(),i-1);
            if(i!=n) insert(1,i);
        }printf("%lld\n",f[n]);
        return 0;
    }
}
signed main(){return yspm::main();}

今天見到了最傻逼的事情,抱歉罵人,但是真的要說髒字

\(define\)\(min,max\) 然後超時除錯

結果就是這裡的問題,乖乖手寫去吧