这是一道有技巧的斜率优化题。(不会斜率优化,请戳这里)
设 a i , b i 分 别 为 第 i 块 土 地 的 长 、 宽 a_i,b_i分别为第i块土地的长、宽 ai,bi分别为第i块土地的长、宽。
首先,我们按长为第一关键字,宽为第二关键字排序,使得长为非下降序列,当长相等时,宽为非下降序列。因为如果一块土地长,宽都大,就可以把比它长宽都小的土地吃掉,所以我们排序后,再删去一些不需要保留的土地,使得所有土地的宽单调递减。
#define a(i) a[i].a
#define b(i) a[i].b
struct node
{
ll a,b;
bool operator <(node c)const{return a==c.a?b<c.b:a<c.a;}//这是重载运算符
}a[N];
//bool cmp(node x,node y){return x.a==y.a?x.b<y.b:x.a<y.a;}
//重载运算符不会打,那就只打上面的一行。
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&a(i),&b(i));
sort(a+1,a+n+1);
int t=1;
for(int i=2;i<=n;i++)
{
while(t&&b(i)>=b(t))t--;
a[++t]=a[i];
}
n=t;
为什么要这样处理呢? 证明:
若
i
<
j
<
k
且
a
i
<
a
j
<
a
k
且
b
i
>
b
j
>
b
k
i<j<k且a_i<a_j<a_k且b_i>b_j>b_k
i<j<k且ai<aj<ak且bi>bj>bk,我们选择
i
,
k
为
一
块
,
j
为
一
块
的
话
i,k为一块,j为一块的话
i,k为一块,j为一块的话,费用为
a
k
∗
b
i
+
a
j
∗
b
j
a_k*b_i+a_j*b_j
ak∗bi+aj∗bj。但是,
把
i
,
j
,
k
分
成
一
块
的
费
用
只
需
a
k
∗
b
i
。
把i,j,k分成一块的费用只需a_k*b_i。
把i,j,k分成一块的费用只需ak∗bi。
所以,我们排序、删土地后,要取连续的一段。
接着,我们设
f
[
i
]
f[i]
f[i]表示对前
i
i
i块土地分成若干个连续块的最小费用。
则有:
f
[
i
]
=
m
i
n
f
[
j
−
1
]
+
a
i
∗
b
j
(
0
<
j
<
i
)
f[i]=min~~{f[j-1]+a_i*b_j}~~~(0<j<i)
f[i]=min f[j−1]+ai∗bj (0<j<i)
把它转化为
y
=
k
x
+
b
y=kx+b
y=kx+b的形式:
f
[
j
−
1
]
=
(
−
a
i
)
∗
b
j
+
f
[
i
]
f[j-1]=(-a_i)*b_j+f[i]
f[j−1]=(−ai)∗bj+f[i]
y
=
k
x
+
b
~~~~~~~y~~~~~=~~~~~k~~~~~~x~~+~~b
y = k x + b
认真看链接的同学知道用斜率优化的题有以下性质:
1.
斜率 ( k , 即 本 题 中 的 − a i ) (k,即本题中的-a_i) (k,即本题中的−ai)具有单调性.
2.
横坐标 ( x ) (x) (x)单调递增
3.
决策点(继承点)有序。
一般来说,当前处理的状态(如此题中的
f
[
i
]
f[i]
f[i])前面符号正负最好与取最大值还是最小值有关。
对笔者而言,取最小值时前面符号最好为正,反之最好为负。
但是,上面的
b
j
b_j
bj并不单调递增啊。
没事,我们可把上式变为:
f
[
j
−
1
]
=
a
i
∗
(
−
b
j
)
+
f
[
i
]
f[j-1]=a_i*(-b_j)+f[i]
f[j−1]=ai∗(−bj)+f[i]
这样,
k
,
x
都
单
调
递
增
,
且
满
足
f
[
i
]
前
面
是
正
号
,
就
可
以
用
斜
率
优
化
了
。
k,x都单调递增,且满足f[i]前面是正号,就可以用斜率优化了。
k,x都单调递增,且满足f[i]前面是正号,就可以用斜率优化了。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define a(i) a[i].a
#define b(i) a[i].b
using namespace std;
typedef long long ll;
const int N=5e4+10;
int n,q[N],l,r;
ll f[N];
struct node
{
ll a,b;
bool operator <(node c)const{return a==c.a?b<c.b:a<c.a;}
}a[N];
inline ll x(int i){return -b(i+1);}
inline ll y(int i){return f[i];}
inline bool pd(int i,int j,int k)
{
return (y(j)-y(i))*(x(k)-x(j))<(y(k)-y(j))*(x(j)-x(i));
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld%lld",&a(i),&b(i));
sort(a+1,a+n+1);
int t=1;
for(int i=2;i<=n;i++)
{
while(t&&b(i)>=b(t))t--;
a[++t]=a[i];
}
n=t;
l=r=1;q[1]=0;
for(int i=1;i<=n;i++)
{
while(l<r&&y(q[l+1])-y(q[l])<=a(i)*(x(q[l+1])-x(q[l])))l++;
int j=q[l];
f[i]=f[j]+b(j+1)*a(i);
while(l<r&&!pd(q[r-1],q[r],i))r--;
q[++r]=i;
}
printf("%lld\n",f[n]);
return 0;
}