题意:n 个墙壁 m 个粉刷匠,每个墙壁至多能被刷一次,每个粉刷匠要么不刷,要么就粉刷包含第 Si 块的长度不超过 Li 的连续墙壁 (中间可不刷),每一块被刷的墙壁都可获得 Pi 的利润,求最大利润

避免重复粉刷:

首先对 Si 排序并定义 \(f[i][j]\):前 i 个木匠处理到第 j 块木板时的最大利润

此时[j+1,n]保证没被处理以满足无后效性

保证情况一的合法

\[f[i][j]=f[i-1][j]\]

保证情况二的 Si 必须被粉刷

定义处理的区间为[k+1,j]

则 \(f[i][j]=max_kf[i-1][k]+P_i*(j-k)\)

此时要求 Si 必须在[k+1,j]内部,则对 k 加以限定:\(k+1≤S_i≤j\)

连续长度的表示:\(j-k≤L_i\)

中间可空出部分的表示 \(f[i][j]=f[i][j-1]\)

由此可总结转移方程的三部分

前两部分为 \(f[i][j]=max(f[i-1][j],f[i][j-1])\)

最后一部分为 \(f[i][j]=max_{j-L_i≤k≤S_i-1}f[i-1][k]+P_i*(j-k),S_i≤j\)

对于第三部分可改写方程为 \(f[i][j]=max_{j-L_i≤k≤S_i-1}(f[i-1][k]-k*P_i)+j*P_i,S_i≤j\) 以满足单调队列优化

单调队列在 \(i\) 的内循环当中可认为 \(i\) 是常数,也就是说每一层 \(i\) 都需要设立一个只与 \(k\) 有关的单调队列 \(que\),但在实际中只使用一个

\(que\) 每一次的初始化范围由最小的 \(j\) 决定 由 \(S_i≤j\)和\(j-L_i≤k≤S_i-1\)

得 \(S_i-L_i≤k_{init}≤S_i-1\)

此时 \(j=1\) 则得到所有有效的状态 \(k\)

更新过程保证头部 \(val\) 最优,\(k\) 单调递增 (因为后者的更新顺序是从左往右)

注意单调更新时只对头部更新,因为单调范围右边界总是 \(S_i-1\)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#define rep(i,j,k) for(register int i=j;i<=k;i++)
#define rrep(i,j,k) for(register int i=j;i>=k;i--)
#define erep(i,u) for(register int i=head[u];~i;i=nxt[i])
#define iter(i,j) for(int i=0;i<(j).size();i++)
#define print(a) printf("%lld",(ll)a)
#define println(a) printf("%lld\n",(ll)a)
#define printbk(a) printf("%lld ",(ll)a)
#define IOS ios::sync_with_stdio(0)
using namespace std;
const int MAXN = 2e4+11;
const int oo = 0x3f3f3f3f;
typedef long long ll;
ll read(){
    ll x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct XJB{
    int l,p,s;
    bool operator < (const XJB &r) const{
        return s<r.s;
    }
}a[MAXN];
int n,m;
int f[233][MAXN];
inline int cal(int i,int k){
    return f[i-1][k]-a[i].p*k;
}
int main(){
    while(cin>>n>>m){
        rep(i,1,m){
            a[i].l=read();
            a[i].p=read();
            a[i].s=read();
        }
        sort(a+1,a+1+m);
        memset(f,0,sizeof f);
        deque<int> que;
        rep(i,1,m){
            while(!que.empty()) que.pop_back();
            rep(k,max(0,a[i].s-a[i].l),a[i].s-1){ //j-Li<=k<=Si-1,j>=Si
                while(!que.empty()&&cal(i,k)>=cal(i,que.back())) que.pop_back();
                que.push_back(k);
            }
            rep(j,1,n){
                f[i][j]=max(f[i-1][j],f[i][j-1]);
                if(j>=a[i].s){
                    while(!que.empty()&&que.front()<j-a[i].l) que.pop_front();
                    if(!que.empty()) f[i][j]=max(f[i][j],cal(i,que.front())+a[i].p*j);
                }
            }
        }
        println(f[m][n]);
    }
    return 0;
}