标签: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