CodeForces - 1101F Trucks and Cities

Solution

直接枚举 m m m 显然是不现实的。

不过我们发现,卡车的油箱大小和卡车依靠油箱走的最长的一段路是一次函数关系。

而卡车依靠油箱走的最长的一段路是由卡车能将它的路程分为几段决定的。

所以可以转化为 i i i j j j k k k 段路,使得最长的一段路最短。

朴素算法枚举 i , j , k i,j,k i,j,k 和最后一段路的开始 t t t,是 O ( n 4 ) O(n^4) O(n4)

f [ i ] [ j ] = min ⁡ t = 1 j max ⁡ ( f [ i ] [ t ] , d i s ( t , j ) ) f[i][j]=\min_{t=1}^j \max(f[i][t],dis(t,j)) f[i][j]=t=1minjmax(f[i][t],dis(t,j))

但是考虑到 f [ i ] [ t ] f[i][t] f[i][t] t t t 的增大而增大, d i s ( t , j ) dis(t,j) dis(t,j) t t t 的增大而减小。我们应该使这两个玩意尽量均衡(即增加 t t t)。而且, j j j 是逐渐增加的, d i s ( t , j ) dis(t,j) dis(t,j) 又会增加,我们再次增加 t t t,这是最优的。

这样复杂度就减到 O ( n 3 ) O(n^3) O(n3)

Code

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;

const int N = 405, M = 250005;

int n, m, p[N], a[M], b[M], c[M], d[M];
vector <int> id[N];
long long ans, f[N][N][2];

int read() {
	int x = 0, f = 1; char s;
	while((s = getchar()) > '9' || s < '0') if(s == '-') f = -1;
	while(s >= '0' && s <= '9') x = (x << 1) + (x << 3) + (s ^ 48), s = getchar();
	return x * f;
}

int main() {
	n = read(), m = read();
	for(int i = 1; i <= n; ++ i) p[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]);
		id[d[i]].push_back(i);
	}
	for(int i = 1; i <= n; ++ i)
		for(int j = i; j <= n; ++ j)
			f[i][j][0] = p[j] - p[i];
	for(int k = 1; k < n; ++ k) {
		for(int i = 0; i < id[k].size(); ++ i)
			ans = max(ans, f[a[id[k][i]]][b[id[k][i]]][k + 1 & 1] * c[id[k][i]]);
		for(int i = 1; i <= n; ++ i) {
			f[i][i][k & 1] = 0;
			int pos = i;
			for(int j = i + 1; j <= n; ++ j) {
				while(f[i][pos][k + 1 & 1] < p[j] - p[pos]) ++ pos;
				f[i][j][k & 1] = min(f[i][pos][k + 1 & 1], 0ll + p[j] - p[pos - 1]);
			}
		}
	}
	printf("%lld\n", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值