[bzoj1597][Usaco2008 Mar]土地购买

农夫John准备扩大他的农场,他正在考虑N (1 <= N <= 50,000) 块长方形的土地. 每块土地的长宽满足(1 <= 宽 <= 1,000,000; 1 <= 长 <= 1,000,000). 每块土地的价格是它的面积,但FJ可以同时购买多快土地. 这些土地的价格是它们最大的长乘以它们最大的宽, 但是土地的长宽不能交换. 如果FJ买一块3x5的地和一块5x3的地,则他需要付5x5=25. FJ希望买下所有的土地,但是他发现分组来买这些土地可以节省经费. 他需要你帮助他找到最小的经费.

题解:先排序一下去掉没用的矩形,然后就可以斜率优化啦。

f[i]=min(f[j]+b[j+1]*a[i])     

f[j]+b[j+1]*a[i]<f[k]+b[k+1]*a[i]

$a[i] > frac{f[j]-f[k]}{b[k+1]-b[j+1]}$

#include<iostream>
#include<cstdio>
#include<algorithm>
#define MN 50000
#define ll long long
#define int long long
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,m;
struct sq{int a,b;}s[MN+5],l[MN+5];
ll f[MN+5];
bool cmp(sq x,sq y){return x.a<y.a||(x.a==y.a&&x.b<y.b);}
int q[MN+5],top,tail;

double calc(int x,int y)
{
    return (double)(f[x]-f[y])/(s[x+1].b-s[y+1].b);
}

int get(int x)
{
    while(top>tail&&-x<calc(q[tail+1],q[tail])) tail++;
    return q[tail];
}

void ins(int x)
{
    while(top>tail&&calc(x,q[top])>calc(q[top],q[top-1]))--top;
    q[++top]=x;
}

main()
{
    n=read();
    for(int i=1;i<=n;i++)l[i].a=read(),l[i].b=read();
    sort(l+1,l+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        while(m&&l[i].b>=s[m].b) --m;
        s[++m]=l[i];
    }
    q[top=tail=1]=0;//f[1]=1LL*s[1].a*s[1].b;
    for(int i=1;i<=m;i++)
    {
        int from=get(s[i].a);
        f[i]=f[from]+1LL*s[from+1].b*s[i].a;
        ins(i);
    }
    cout<<f[m];
    return 0;
}
原文地址:https://www.cnblogs.com/FallDream/p/bzoj1597.html