题意
给你a[]个点的x轴位置,代表加油站
m个询问,每次有一辆卡车,输入s,f,c,r四个点
代表从a[s]出发到a[f]允许中途在r个加油站停留(不包括s和f)并加满油
c为单位距离的油的消耗体积
求m辆卡车的油箱最小体积V是多少
思路来源
https://www.cnblogs.com/xht37/p/10263908.html
题解
首先,等价于把s到f分成(r+1)份,只能在加油站点分的时候,求最长距离的最小值
dp[i][j][k]代表从i到j加油站允许停k次的最大距离的最小值
不停时,dp[i][j][0]=a[j]-a[i]
dp[i][j][k]=max(dp[i][w][k-1],a[j]-a[w]);//w为中转站
类似区间dp,但每次只后缀一个
显然,i,k固定时,j越靠右,w就会越靠右,w满足单调性
想想把一段分成两份的中点w,和把一段分成三份三分之二处那个等分点w,显然
此外,这个距离存在最小值,是一个开口向上的凸函数,
显然,w在靠近i的时候,前半段a[j]-a[w]大,
w在靠近j的时候,后半段dp[i][w][k-1](显然>=dp[i][w-1][k-1])大,
技巧:中间找这个最小值,我们只要从i线性推进w,比下一个值小就一直往前推就好了。
这样就是O(n³)
另外凸函数似乎也可以三分找w然后w单增O(n²logn)??
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <functional>
const int INF=0x3f3f3f3f;
const int maxn=405;
const int mod=1e9+7;
const int MOD=998244353;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int>
#define si set<int>
#define pii pair<int,int>
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%I64d",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof0(a))
using namespace std;
int n,m,a[maxn];
int dp[maxn][maxn][maxn];
//dp[i][j][k]代表从i到j需要k次加油的最大距离的最小值
int main()
{
sci(n),sci(m);
rep(i,1,n)sci(a[i]);
rep(i,1,n)rep(j,i,n)dp[i][j][0]=a[j]-a[i];
rep(k,1,n)
{
rep(i,1,n)
{
int w=i;
rep(j,i,n)
{
while(w<j&&max(dp[i][w][k-1],a[j]-a[w])>max(dp[i][w+1][k-1],a[j]-a[w+1]))w++;
dp[i][j][k]=max(dp[i][w][k-1],a[j]-a[w]);
}
}
}
ll ans=0;
rep(i,1,m)
{
int s,f,r;ll c;
sci(s),sci(f),scll(c),sci(r);
ans=max(ans,dp[s][f][r]*(ll)c);
}
printf("%I64d\n",ans);
return 0;
}