这是一道有技巧的斜率优化题。(不会斜率优化,请戳这里)
设。
首先,我们按长为第一关键字,宽为第二关键字排序,使得长为非下降序列,当长相等时,宽为非下降序列。因为如果一块土地长,宽都大,就可以把比它长宽都小的土地吃掉,所以我们排序后,再删去一些不需要保留的土地,使得所有土地的宽单调递减。
#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;
为什么要这样处理呢? 证明:
若,我们选择,费用为。但是,
所以,我们排序、删土地后,要取连续的一段。
接着,我们设表示对前块土地分成若干个连续块的最小费用。
则有:
把它转化为的形式:
认真看链接的同学知道用斜率优化的题有以下性质:
1.
斜率具有单调性.
2.
横坐标单调递增
3.
决策点(继承点)有序。
一般来说,当前处理的状态(如此题中的)前面符号正负最好与取最大值还是最小值有关。
对笔者而言,取最小值时前面符号最好为正,反之最好为负。
但是,上面的并不单调递增啊。
没事,我们可把上式变为:
这样,
代码:
#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
",f[n]);
return 0;
}