2019 ICPC Asia Xuzhou Regional K K-rectangle【ICPC徐州J】【DP】【斜率优化】【可回退化凸包】

题目大意

  平面上有 n n n 个点,求用若干个底落在 x x x轴的矩形来覆盖所有点,并且使得代价最小.
  其中一个 w ⋅ h w\cdot h wh矩形的代价为 ( w + k ) ⋅ h (w+k)\cdot h (w+k)h k k k为大于 0 0 0的常数。
n ≤ 4 ⋅ 1 0 5 , k ≤ 1 0 6 n\le 4\cdot 10^5,k\le 10^6 n4105k106

题解

 这种题最容易想到的就是 d p dp dp d p [ i ] dp[i] dp[i]表示覆盖前 i i i个点的最小代价,那么转移显然有: d p [ i ] = d p [ j − 1 ] + m a x { h [ j ] , h [ j + 1 ] , . . h [ i ] } ⋅ ( x [ i ] − x [ j ] + k ) dp[i]=dp[j-1]+max\{h[j],h[j+1],..h[i]\}\cdot(x[i]-x[j]+k) dp[i]=dp[j1]+max{h[j],h[j+1],..h[i]}(x[i]x[j]+k)
 暴力转移的复杂度显然是不对的,考虑怎么优化。
 如果使用单调栈去维护以 i i i结尾的最大值相同的连续段,那么容易知道总的连续段个数是 O ( n ) O(n) O(n)的,而在同一个连续段中 m a x max max是固定的,则有: d p [ i ] = d p [ j − 1 ] + m a x ⋅ ( x [ i ] − x [ j ] + k ) dp[i]=dp[j-1]+max\cdot(x[i]-x[j]+k) dp[i]=dp[j1]+max(x[i]x[j]+k)即: d p [ i ] = m a x ⋅ x [ i ] + { d p [ j − 1 ] + m a x ⋅ ( k − x [ j ] ) } = m a x ⋅ x [ i ] + f ( m a x ) dp[i]=max\cdot x[i]+\{dp[j-1]+max\cdot(k-x[j])\}=max\cdot x[i]+f(max) dp[i]=maxx[i]+{dp[j1]+max(kx[j])}=maxx[i]+f(max)
 而在一个连续段中, f ( m a x ) = d p [ j − 1 ] + m a x ⋅ ( k − x [ j ] ) f(max)=dp[j-1]+max\cdot(k-x[j]) f(max)=dp[j1]+max(kx[j])是一个斜率优化的形式,线段树维护凸包即可查询同一连续段中 m a x max max对应的最优的 j j j.

 那么现在的问题就是在单调栈后插入/删除若干个连续段,要支持查询 m a x ⋅ x [ i ] + f ( m a x ) max\cdot x[i]+f(max) maxx[i]+f(max)的最小值。这也是一个斜率优化的形式,所以肯定是凸包无疑了,而由于只会在最后面加入/删除,所以凸包只要支持可回退化即可(注意在凸包加点的时候要二分来加点)

  即该题只需要维护两个凸包即可,复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)(注意在线段树上查询最值的时候不要二分,由于每次查询的 x [ i ] x[i] x[i]是单调的,利用决策单调性维护一个指针扫过去即可).

#include<bits/stdc++.h>
#define maxh 20
#define maxn 400050
using namespace std;
typedef long long LL;

const int lim=1e6;
const LL inf=1LL<<60;

int n,m;

struct node {
    LL x,y;
    node(LL _x=0,LL _y=0)   {
        x=_x,y=_y;
    }
    node operator - (const node& N) const   {
        return node(x-N.x,y-N.y);
    }
    LL operator * (const node& N) const {
        return x*N.y-y*N.x;
    }
    LL operator ()(const LL& k) const   {
        return k*x-y;
    }
} P[maxn];

ostream& operator << (ostream& os,const node& N)    {
    os<<"("<<N.x<<","<<N.y<<")";
    return os;
}

namespace SegT  {
    const int M=524288;

    int pointer[M<<1];
    vector<node> T[M<<1];
    
    void insert(int i,node p)   {
        p.y=-p.y;
        T[i+M-1].push_back(p);
        for (int k=i+M-1,len=1;k>1&&(k&1);)   {
            k>>=1,len<<=1;
            for (int j=i-len+1;j<=i;++j)  {
                node p=T[j+M-1].back();
                while (T[k].size()>1&&(T[k].back()-p)*(T[k][T[k].size()-2]-p)>=0)  T[k].pop_back();
                T[k].push_back(p);
            }
        }
    }

    LL query(int rt,LL k) {
        int &ptr=pointer[rt];
        while (ptr+1<T[rt].size()&&T[rt][ptr](k)>T[rt][ptr+1](k))    ++ptr;
        return T[rt][ptr](k);
    }

    LL query(int rt,int l,int r,int a,int b,LL k) {
        if (a>r||l>b)   return inf;
        if (a<=l&&r<=b) return query(rt,k);
        int mid=(l+r)>>1;
        return min(query(rt<<1,l,mid,a,b,k),query(rt<<1|1,mid+1,r,a,b,k));
    }

    LL query(int l,int r,LL k)  {   return query(1,1,M,l,r,k);  }
}

namespace DCH   {
    int n;
    node P[maxn];

    pair<int,int> backup[maxn];
    int stk[maxn],tp;

    void pushback(node p) {
        p.y=-p.y;
        P[++n]=p;

        int l=1,r=tp;
        while (l<r) {
            int mid=(l+r+1)>>1;
            if ((P[stk[mid]]-p)*(P[stk[mid-1]]-p)<0)
                l=mid;
            else
                r=mid-1;
        }

        backup[n]=make_pair(stk[r+1],tp);
        stk[tp=r+1]=n;
    }

    void rollback() {
        assert(n);
        stk[tp]=backup[n].first;
        tp=backup[n].second;
        --n;
    }

    LL query(LL k)  {
        int l=1,r=tp;
        while (l<r) {
            int mid=(l+r)>>1;
            if (P[stk[mid]](k)>P[stk[mid+1]](k))
                l=mid+1;
            else
                r=mid;
        }
        return P[stk[l]](k);
    }
}

int stk[maxn],tp;

LL dp[maxn];

int main()  {
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
        scanf("%lld%lld",&P[i].x,&P[i].y);
    
    for (int i=1;i<=n;++i)  {
        SegT::insert(i,node(m-P[i].x,dp[i-1]));
        for (;tp&&P[stk[tp]].y<=P[i].y;--tp)
            DCH::rollback();
        DCH::pushback(node(P[i].y,SegT::query(stk[tp]+1,i,P[i].y)));
        stk[++tp]=i;
        dp[i]=DCH::query(P[i].x);
    }

    printf("%lld\n",dp[n]);
    cerr<<clock()<<endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值