解题思路
设 f [ i , j ] f[i,j] f[i,j]表示前i个木匠粉刷了前j个木板(可以有木板不粉刷)可知:
如果新加入的一个木匠要粉刷一段木块,设他粉刷的区间为
[
k
+
1
,
j
]
[k+1,j]
[k+1,j]
得
f
[
i
]
[
j
]
=
m
a
x
j
−
l
i
<
=
k
<
s
i
(
f
[
i
−
1
,
k
]
+
p
i
∗
(
j
−
k
)
)
;
(
j
>
=
s
i
)
f[i][j]=max_{j-l_i<=k<s_i}(f[i-1,k]+p_i*(j-k)) ;(j>=s_i)
f[i][j]=maxj−li<=k<si(f[i−1,k]+pi∗(j−k));(j>=si)
我们把j提出来,是后面的式子只和k有关:
得
f
[
i
]
[
j
]
=
p
i
∗
j
+
m
a
x
j
−
l
i
<
=
k
<
s
i
(
f
[
i
−
1
,
k
]
−
p
i
∗
k
)
;
(
j
>
=
s
i
)
f[i][j]=p_i*j+max_{j-l_i<=k<s_i}(f[i-1,k]-p_i*k); (j>=s_i)
f[i][j]=pi∗j+maxj−li<=k<si(f[i−1,k]−pi∗k);(j>=si)
最后用单调队列维护 f [ i − 1 , k ] − p i ∗ k f[i-1,k]-p_i*k f[i−1,k]−pi∗k的最大值即可。
//代码中的 k 表 示 i , i 分 别 表 示 k 和 j k表示i,i分别表示k和j k表示i,i分别表示k和j
代码
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
int n,m;
ll dp[20010],x[20010],q[20010],w[20010];
struct c{
int l,p,s;
}a[20010];
bool cmp(c l,c r){
return l.s<r.s;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a[i].l,&a[i].p,&a[i].s);
sort(a+1,a+m+1,cmp);
for(int k=1;k<=m;k++)
{
int s=a[k].s,p=a[k].p,l=a[k].l;
int mn=max(0,s-l),mx=min(n,s+l);
int h=1,t=0;
for(int i=mn;i<=mx;i++)
x[i]=dp[i]-p*i;
for(int i=mn;i<s;i++)//从上一个木匠的k转移过来时,枚举k成立的范围
{
while(h<=t&&q[t]<=x[i])t--;
q[++t]=x[i];
w[t]=i;
}
for(int i=s;i<=mx;i++)//当前木匠可粉刷到的范围,枚举j成立的范围
{
while(h<=t&&w[h]<i-l)h++;
if(h>t)break;
dp[i]=max(dp[i],q[h]+p*i);
}
for(int i=2;i<=n;i++)
dp[i]=max(dp[i],dp[i-1]);
}
printf("%lld",dp[n]);
}