Educational Codeforces Round 58 (Rated for Div. 2) F. Trucks and Cities(区间dp+单调优化)

题意

给你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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值