一、题目
二、解法
可以把加油理解成划段,那么题目转化成求点 [ i , j ] [i,j] [i,j]之间划 k k k段最大的段的最小值是多少,对于所有的车算出这个值乘以 c c c,然后取最大值就是打啊。
设
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]为
[
i
,
j
]
[i,j]
[i,j]划
k
k
k段的最大段最小值,转移:
d
p
[
i
]
[
j
]
[
k
]
=
max
(
d
p
[
i
]
[
l
]
[
k
−
1
]
,
t
[
j
]
−
t
[
l
]
)
dp[i][j][k]=\max(dp[i][l][k-1],t[j]-t[l])
dp[i][j][k]=max(dp[i][l][k−1],t[j]−t[l])这样做是
O
(
n
4
)
O(n^4)
O(n4)的,观察式子,发现
d
p
[
i
]
[
l
]
[
k
−
1
]
dp[i][l][k-1]
dp[i][l][k−1]随
l
l
l单调递增,
t
[
j
]
−
t
[
l
]
t[j]-t[l]
t[j]−t[l]随
l
l
l单调递减,我们可以维护一个边界
l
l
l,使得
l
l
l处
d
p
[
i
]
[
l
]
[
k
−
1
]
dp[i][l][k-1]
dp[i][l][k−1]恰好更大,
l
−
1
l-1
l−1处
t
[
j
]
−
t
[
l
]
t[j]-t[l]
t[j]−t[l]恰好更大,容易发现这是一个指针的移动,所以时间复杂度优化到了
O
(
n
3
)
O(n^3)
O(n3)
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 405;
const int N = 250005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,t[M],a[N],b[N],c[N],d[N],dp[2][M][M];
vector<int> v[M];long long ans;
signed main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
t[i]=read();
for(int i=1;i<=m;i++)
{
a[i]=read();b[i]=read();
c[i]=read();d[i]=read()+1;
d[i]=min(d[i],b[i]-a[i]);
v[d[i]].push_back(i);
}
memset(dp,0x3f,sizeof dp);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
dp[1][i][j]=t[j]-t[i];
for(int k=1;k<n;k++)
{
for(int i=0;i<v[k].size();i++)
{
int x=v[k][i];
ans=max(ans,1ll*dp[k&1][a[x]][b[x]]*c[x]);
}
for(int i=1;i<=n;i++)
dp[(k+1)&1][i][i]=0;
for(int i=1;i<=n;i++)
{
int l=i;
for(int j=i+1;j<=n;j++)
{
while(dp[k&1][i][l]<t[j]-t[l]) l++;
dp[(k+1)&1][i][j]=min(dp[k&1][i][l],t[j]-t[l-1]);
}
}
}
printf("%lld\n",ans);
}