码迷,mamicode.com
首页 > 其他好文 > 详细

P2900 [USACO08MAR]Land Acquisition G

时间:2020-06-18 01:03:41      阅读:60      评论:0      收藏:0      [点我收藏+]

标签:data   turn   span   代码   条件   long   std   最大的   return   

https://www.luogu.com.cn/problem/P2900
\(n\) 块地,给出每块的长和宽
每次可以购买一个或多个地,此次购买的代价是这些地中最大的宽乘以最大的长
问最小花费多少代价能把所有地都买到

斜率优化dp

首先想到,如果有一个地比另一个地的长和宽都要小,显然可以不考虑这块地了
忽略这种地的过程,可以按 \(l\) 为第一关键字,\(w\) 为第二来升序排个序,然后用栈维护一下就好
是这样一个过程:

std::sort(a+1,a+1+n,cmp);
for(reg int i=1;i<=n;i++){
	while(top&&b[top].w<=a[i].w&&b[top].l<=a[i].l) top--;
	b[++top]=(data){a[i].w,a[i].l};
}

这样处理完以后(按照在 b 数组里的顺序来看),\(l\) 是单调不减的
所以 \(w\) 应该是单调减,因为如果它不变或者递增的话,就会被当作不用处理的地来忽略掉

然后一次购买的多块地,在这种排序方式下,一定是连续的
假设购买了 \(x,x+2\) 两块地,那么花费是 \(w_x l_{x+2}\),然后又因为 \(w_{x+1}<w_x,l_{x+1}\le l_{x+2}\)
所以完全可以在不增加任何代价的情况下将第 \(x+1\) 块地包括进去

那么可以写出dp的转移方程了 \(f_i=\min\{f_j+w_{j+1} l_i\}\)
但这样是 \(O(n^2)\) 的,还需要个斜率优化
首先可以确定 \(b=f_i\),比较习惯最小化截距,不过题解区里好像还有最小化 \(y\) 的?
然后 \(w_{j+1} l_i\) 肯定是解析式里的 \(kx\)
\(k\) 应该和 \(i\) 有关,所以 \(k=l_i,x=-w_{j+1}\)
那么 \(y\) 就是 \(f_j\)

所以:\(f_j=l_i(-w_{j+1})+f_i\)
因为 \(l\) 递增,所以符合 \(k\) 递增的条件,可以不用二分
\(w\) 递减,所以 \(-w\) 递增,也符合 \(x\) 递增的要求

似乎就可以了,放上代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<‘0‘||c>‘9‘){if(c==‘-‘) y=0;c=std::getchar();}
	while(c>=‘0‘&&c<=‘9‘){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 50005
struct data{
	LL w,l;
}a[N],b[N];
int top,n;
int tail,head;
int q[N];
LL f[N];
inline int cmp(data a,data b){
	return a.l==b.l?a.w<b.w:a.l<b.l;
}
inline LL get_x(int i){return -b[i+1].w;}
inline LL get_y(int i){return f[i];}
int main(){
	n=read();
	for(reg int i=1;i<=n;i++) a[i].w=read(),a[i].l=read();
	std::sort(a+1,a+1+n,cmp);
	for(reg int i=1;i<=n;i++){
		while(top&&b[top].w<=a[i].w&&b[top].l<=a[i].l) top--;
		b[++top]=(data){a[i].w,a[i].l};
	}
	n=top;
	tail=head=0;q[0]=0;
	for(reg int i=1;i<=n;i++){//k=l[i]
		while(tail<head&&
		get_y(q[tail+1])-get_y(q[tail])<=b[i].l*(get_x(q[tail+1])-get_x(q[tail]))) tail++;
		int j=q[tail];
		f[i]=f[j]+b[j+1].w*b[i].l;
		while(tail<head&&(get_y(q[head])-get_y(q[head-1]))*(get_x(i)-get_x(q[head]))
		>=(get_y(i)-get_y(q[head]))*(get_x(q[head])-get_x(q[head-1]))) head--;
		q[++head]=i;
	}
	std::printf("%lld",f[n]);
	return 0;
}

P2900 [USACO08MAR]Land Acquisition G

标签:data   turn   span   代码   条件   long   std   最大的   return   

原文地址:https://www.cnblogs.com/suxxsfe/p/13155273.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!