题面描述
思路
首先我们先按照 s i s_i si来排序,保证工匠的有序性。
F i , j F_{i,j} Fi,j为现在是第 i i i个工匠粉刷前 j j j个木板(有的木板可以不刷)
状态转移方程为:
F
i
,
j
=
max
{
F
i
−
1
,
j
(
这
个
工
匠
太
懒
了
,
不
干
活
)
F
i
,
j
−
1
(
这
个
工
匠
不
刷
第
j
个
木
板
)
F
i
−
1
,
k
+
p
i
∗
(
j
−
k
)
,
j
≥
s
i
,
j
−
l
i
≤
k
≤
s
i
−
1
}
\large\ F_{i,j}=\max\begin{Bmatrix}F_{i-1,j}(这个工匠太懒了,不干活)\\F_{i,j-1}(这个工匠不刷第j个木板)\\F_{i-1,k}+p_i*(j-k),j\ge s_i,j-l_i\le k\le s_i-1\end{Bmatrix}
Fi,j=max⎩⎪⎨⎪⎧Fi−1,j(这个工匠太懒了,不干活)Fi,j−1(这个工匠不刷第j个木板)Fi−1,k+pi∗(j−k),j≥si,j−li≤k≤si−1⎭⎪⎬⎪⎫
主要如何优化第三个转移方程。
变式可以发现:
F i − 1 , k + p i ∗ ( j − k ) = F i − 1 , k − p i ∗ k + p i ∗ j F_{i-1,k}+p_i*(j-k)=F_{i-1,k}-p_i*k+p_i*j Fi−1,k+pi∗(j−k)=Fi−1,k−pi∗k+pi∗j
将 F i − 1 , k − p i ∗ k F_{i-1,k}-p_i*k Fi−1,k−pi∗k看作一个整体,就可以进行单调队列优化了。
AC code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define gc getchar()
using namespace std;
const int K=110;
const int N=1.6e4+10;
struct node{int s,l,p;}a[K];
bool cmp(node x,node y){return x.s<y.s;}
int f[K][N],q[N];
inline int calc(int i,int k){return f[i-1][k]-a[i].p*k;}
int main()
{
int n,m;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 i=1;i<=m;i++)
{
int head=1,tail=0;
for(int k=max(0,a[i].s-a[i].l);k<=a[i].s-1;k++)
{
while(head<=tail&&calc(i,k)>calc(i,q[tail]))--tail;//维护一个下降队列
q[++tail]=k;
}
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(head<=tail&&q[head]<j-a[i].l)++head;//踢掉不合法的
if(head<=tail)f[i][j]=max(f[i][j],calc(i,q[head])+a[i].p*j);
}
}
}
printf("%d\n",f[m][n]);
return 0;
}