1597 [Usaco2008 Mar]土地购买
题目链接
Time Limit 10 Sec Memory Limit 162 MB
Submit 6013 Solved 2278
Description
农夫John准备扩大他的农场,他正在考虑N (1 = N = 50,000) 块长方形的土地. 每块土地的长宽满足(1 = 宽
= 1,000,000; 1 = 长 = 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价
格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要
付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.
Input
第1行 一个数 N
第2..N+1行 第i+1行包含两个数,分别为第i块土地的长和宽
Output
第一行 最小的可行费用.
Sample Input
4
100 1
15 15
20 5
1 100
共有4块土地.
Sample Output
500
FJ分3组买这些土地
第一组100x1,
第二组1x100,
第三组20x5 和 15x15 plot.
每组的价格分别为100,100,300, 总共500.
HINT
Source
Gold
题解
斜率优化,这篇写得相对简单一些,如果初学可以看看luogu-P2120 [ZJOI2007]仓库建设
或 zzk大佬的博客,这篇写得很详细,我也是看这篇学会的。
首先我们有一个贪心的想法:如果一个土地 i 的长和宽(x[i] 和 y[i])都小于另一块土地 j (x[i] 和 y[j]),那么这块土地肯定不用单独购买(也就是免费了)。
排序合并后(x 为第一关键字,y 第二,全部递减顺序),x[i] 是递增的,y[i]是递减的(想一想为什么?)
所以
f[i]=min{f[j]+x[j+1]∗y[i]}
f
[
i
]
=
m
i
n
{
f
[
j
]
+
x
[
j
+
1
]
∗
y
[
i
]
}
设选 j 为前一状态比 k 好其中 j > k。可得
f[j]+x[j+1]∗y[i]<f[k]+x[k+1]∗y[i]
f
[
j
]
+
x
[
j
+
1
]
∗
y
[
i
]
<
f
[
k
]
+
x
[
k
+
1
]
∗
y
[
i
]
移项得斜率式:
(f[j]−f[k])/(x[k+1]−x[j+1])<y[i]
(
f
[
j
]
−
f
[
k
]
)
/
(
x
[
k
+
1
]
−
x
[
j
+
1
]
)
<
y
[
i
]
我们设
g(j,k)=(f[j]−f[k])/(x[k+1]−x[j+1])<y[i]
g
(
j
,
k
)
=
(
f
[
j
]
−
f
[
k
]
)
/
(
x
[
k
+
1
]
−
x
[
j
+
1
]
)
<
y
[
i
]
分类讨论一下能得到当
g(i,j)<=g(j,k)
g
(
i
,
j
)
<=
g
(
j
,
k
)
时 j 没意义(其中 i>j>k )。
讨论的时候注意一下
g(i,j)=g(j,i)
g
(
i
,
j
)
=
g
(
j
,
i
)
,顺序不影响这个值,我自己经常会认为
g(i,j)=−g(j,i)
g
(
i
,
j
)
=
−
g
(
j
,
i
)
,希望大家不要和我犯同样的错误了QWQ。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define DB double
using namespace std;
const int maxn=5e4+5;
int n,m,q[maxn],hea,til;
LL f[maxn];
struct js{
LL x,y;
bool operator <(const js &b)const{return x>b.x||(x==b.x&&y>b.y);}
}a[maxn];
DB xie(int i,int j){return (f[i]-f[j])*1.0/(a[j+1].x-a[i+1].x);}
int read()
{
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
int main()
{
n=read();
for (int i=1;i<=n;i++) a[i]=(js){read(),read()};
sort(a+1,a+n+1);
for (int i=1,lst=0;i<=n;i++)
if (a[lst].y<a[i].y) a[++m]=a[i],lst=i;
for (int i=1;i<=m;i++)
{
while (hea<til&&xie(q[hea+1],q[hea])<a[i].y) hea++;
f[i]=f[q[hea]]+(LL)a[q[hea]+1].x*a[i].y;
while (hea<til&&xie(q[til],i)<=xie(q[til-1],q[til])) til--;
q[++til]=i;
}
printf("%lld",f[m]);
return 0;
}