Codeforces 1083E The Fair Nut and Rectangles

Codeforces 1083E The Fair Nut and Rectangles

题目链接

https://codeforces.com/contest/1083/problem/E

题目大意

给你一个 $n$ 个左下角和右上角为 $(0, 0)$ 和 $(x_i, y_i)$ 的矩形,每个矩形有一个权值 $a_i$。问选出若干个矩形,最大化这些矩形的面积并与这些矩形的权值和的差。限制条件为,没有嵌套的矩形

题目分析

从限制条件中可以得知,任何两个矩形,横坐标大的那个纵坐标一定小,,反之亦然,且没有坐标相同的两个矩形。由此可以想到,首先对所有的矩形按照横坐标由小到大排序。之后考虑使用 $DP$ 来解决这道题。则可以推出递推式

$$f[i] = max\{f[j] – x_i y_j + x_i y_i – a[i]\} (1 \leq j < i)$$

但是此时时间复杂度是 $O(N ^ 2)$ 的。问题在于,每个新的矩形加入的时候 $x_i$ 都会影响递推式中的 $x_i y_i$ 这项与 $j$ 矩形有关的项。但注意到,$f[j] – x_i y_j$ 事实上是个一次的函数,后面有关 $i$ 矩阵的项为定值,因此可以考虑使用斜率优化。令 $y = (-y_j)x + (f[j])$,那么 $f[i]$ 的最大取值即为前面所有直线以及 $y = 0$ 直线中,在 $x = x_i$ 处取值最大的直线所对应的矩阵所转移来的。由于所有的直线的斜率具备单调性,可以使用双端队列来维护,求解即可。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>

using namespace std;

const int maxn = 1e6 + 5;

int n, m, t;

long long ans = 0;

struct ex {
	long long x;
	long long y;
	long long a;
};

ex p[maxn];

struct queue {
	long long k[maxn];
	long long b[maxn];
	int l, r;
	
	double getp(long long ka, long long ba, long long kb, long long bb){
		return (double)(bb - ba) / (double)(ka - kb);
	}
	
	int push(long long kk, long long bb){
		while(r - l + 1 > 1 and getp(k[r], b[r], kk, bb) > getp(k[r - 1], b[r - 1], k[r], b[r])){
			r--;
		}
		
		r++;
		k[r] = kk;
		b[r] = bb;
		
		return 0;
	}
	
	int pop(long long x){
		while(r - l + 1 > 1 and k[l] * x + b[l] < k[l + 1] * x + b[l + 1]){
			l++;
		}
		
		return 0;
	}
	
	long long front(long long x){
		if(r - l + 1 <= 0){
			return 0;
		}
		return k[l] * x + b[l];
	}
	
	queue(){
		r = 0, l = 1;
	}
};

queue q;

long long read(){
	long long x;
	scanf("%lld", &x);
	return x;
}

bool cmp(ex x, ex y){
	return x.y > y.y;
}

int main(){
	int i, j;
	long long tmp;
	
	n = read();
	
	for(i=1;i<=n;i++){
		p[i].x = read();
		p[i].y = read();
		p[i].a = read();
	}
	
	sort(p + 1, p + n + 1, cmp);
	
	for(i=1;i<=n;i++){
		q.pop(p[i].y);
		tmp = q.front(p[i].y) + p[i].x * p[i].y - p[i].a;
		tmp = max(tmp, p[i].x * p[i].y - p[i].a);
		ans = max(ans, tmp);
		q.push(-p[i].x, tmp);
	}
	
	printf("%lld\n", ans);
	
	return 0;
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据