masodik - 凸包 - 斜率优化

题目大意:从(0,0)走到(n,m)每次向右或者向上走一步。在第i行向右走代价是ri,列同理。求最小代价。1e5。
题解:考虑答案路径,假设当前点先向上走了x步每步代价c1,然后向右走了y步每步代价r1。考虑为啥不是先向右走了y步每步代价r2,然后向上走了x步每步代价c2。这时c1x+r1y<=c2x+r2y,化简后就是(r1-r2)/x<=(c2-c1)/y。换言之如果我们钦定先向上走x步,那么想要把先向上再向右的策略改成先右后上,就必须要找一个dalta(c)/y最小的,而这个是和x无关的,进一步地说就是c的下凸壳的下一个点。同时x为了避免被更改策略,需要做和y相同的事情。此时二者在去比较来确定是否更改策略。那么做法就是每次尝试都往凸包的下一个走,并且选择斜率较小的那一个走。随便维护一下即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define gc getchar()
#define lint long long
#define N 100010
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct P{
    int x,y;P(int _x=0,int _y=0){ x=_x,y=_y; }
    inline P operator=(const P &p) { return x=p.x,y=p.y,*this; }
    inline P operator-(const P &p)const{ return P(x-p.x,y-p.y); }
};vector<P> r,c;
inline lint cross(const P &a,const P &b) { return (lint)a.x*b.y-(lint)a.y*b.x; }
inline lint cross(const P &s,const P &t1,const P &t2) { return cross(t1-s,t2-s); }
inline vector<P> convexHull(const vector<P> &ps)
{
    int n=(int)ps.size();if(n<=2) return ps;
    vector<P> ans(ps.size());int k=0;
    for(int i=0;i<n;ans[k++]=ps[i++])
        while(k>1&&cross(ans[k-2],ans[k-1],ps[i])<=0) k--;
    return ans.resize(k),ans;
}
inline int cmp(const P &a,const P &b) { return cross(a,b)>=0; }
int main()
{
    int n=inn()+1,m=inn()+1;lint ans=0ll;
    r.resize(n);for(int i=0;i<n;i++) r[i]=P(i,inn());
    c.resize(m);for(int i=0;i<m;i++) c[i]=P(i,inn());
    r=convexHull(r),c=convexHull(c);int x=0,y=0;
    while(x<(int)r.size()-1&&y<(int)c.size()-1)
    {
        if(cmp(r[x+1]-r[x],c[y+1]-c[y]))//<=
            ans+=(lint)c[y].y*(r[x+1]-r[x]).x,x++;
        else ans+=(lint)r[x].y*(c[y+1]-c[y]).x,y++;
    }
    if(x<(int)r.size()-1) ans+=c[c.size()-1].y*(n-1ll-r[x].x);
    if(y<(int)c.size()-1) ans+=r[r.size()-1].y*(m-1ll-c[y].x);
    return !printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值