题意:有(N(N<=16000))块木板从左到右排成一行,有(M(M<=100))工匠粉刷木板,每块木板最多被粉刷一次.第(i)个工匠要么不粉刷,要么粉刷包括木板(S_i)在内的长度不超过(L_i)的连续的一段木板,每粉刷一块木板可获得报酬(p_i),求最大总报酬.
分析:先按照(S[i])从小到大排序,保证粉刷的木板是按从小到大的顺序来的,方便我们讨论.
设(f[i][j])表示安排前(i)个工匠粉刷前j块木板能够获得的最大报酬.
(f[i][j]=f[i-1][j])第(i)个工匠什么都不刷.
(f[i][j]=f[i][j-1])第(j)块木板可以不刷.
(f[i][j]=max_{f[i-1][k]+P_i*(j-k)}),当(j-L_i<=k<=S_i-1)时,即第(i)个工匠粉刷第(k+1)到第(j)块木板,因为(k+1<=S_i<=j)((S_i)必须要被刷到),且(j-k<=L_i)(第(i)个工匠最多刷(L_i)块木板).
考虑对该转移式进行优化.(f[i][j]=P_i*j+max_{f[i-1][k]-P_i*k}),维护一个k单调递增,同时(f[i-1][k]-P_i*k)单调递减的队列.
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
int q[16005],f[105][16005];
struct Work{
int l,p,s;
}a[105];
inline bool cmp(const Work &x,const Work &y){return x.s<y.s;}
inline int C(int i,int k){return f[i-1][k]-a[i].p*k;}
int main(){
int n=read(),m=read();
for(int i=1;i<=m;i++)
a[i].l=read(),a[i].p=read(),a[i].s=read();
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++){
int l=1,r=0;
for(int j=max(a[i].s-a[i].l,0);j<=a[i].s-1;j++){
while(l<=r&&C(i,q[r])<=C(i,j))r--;
q[++r]=j;
}
for(int j=1;j<=n;j++){
f[i][j]=max(f[i-1][j],f[i][j-1]);
if(j>=a[i].s){
while(l<=r&&q[l]<j-a[i].l)l++;
if(l<=r)f[i][j]=max(f[i][j],a[i].p*j+C(i,q[l]));
}
}
}
printf("%d
",f[m][n]);
return 0;
}