BZOJ 3190. [JLOI2013]赛车

传送门

首先对于斜率相同的直线,只要保留 $b$ 最大的那些

发现最后那些对答案有贡献的线段是下凹的

把直线按斜率从小到大排序,一条条加入,用单调栈维护当前有贡献的直线

如果当前考虑加入的线和倒数第二条线的交点横坐标小于它与最后一条线的横坐标

或者,当前直线的 $b$ 比上一条直线的 $b$ 大

那么把最后一条线弹出,重复此过程直到不满足上述条件后把此线加入栈

这个画个图就很清楚了

最后的那些直线就是答案了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
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<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=1e4+7;
int n,Top,ans[N];
struct Line {
    ll k,b; int id;
    Line (ll _k=0,ll _b=0,int _id=0) { k=_k,b=_b; id=_id; }
    inline bool operator < (const Line &tmp) const {
        return k!=tmp.k ? k<tmp.k : b>tmp.b;
    }
}d[N],tmp[N],st[N];
inline bool fc(Line A,Line B,Line C) { return (C.b-A.b)*(A.k-B.k)<(B.b-A.b)*(A.k-C.k); }
int main()
{
    n=read();
    for(int i=1;i<=n;i++) d[i].id=i;
    for(int i=1;i<=n;i++) d[i].b=read();
    for(int i=1;i<=n;i++) d[i].k=read();
    sort(d+1,d+n+1); int tot=0;
    for(int i=1;i<=n;i++)
        if(i==1 || (d[i].k!=d[i-1].k) || (d[i].k==d[i-1].k&&d[i].b==d[i-1].b) ) tmp[++tot]=d[i];
    for(int i=1;i<=tot;i++) d[i]=tmp[i]; n=tot;
    for(int i=1;i<=n;i++)
    {
        while(Top>1 && fc(st[Top-1],st[Top],d[i])) Top--;
        while(Top && d[i].b>st[Top].b) Top--;
        st[++Top]=d[i];
    }
    for(int i=1;i<=Top;i++) ans[i]=st[i].id;
    sort(ans+1,ans+Top+1);
    printf("%d\n",Top);
    for(int i=1;i<=Top;i++) printf("%d ",ans[i]);
    printf("\n");
    return 0;
}

 

posted @ 2019-09-13 10:57  LLTYYC  阅读(121)  评论(0编辑  收藏  举报