【计算几何 + LIS】The 2019 Asia Yinchuan First Round Online Programming?- J - Nested Triangles

题目链接https://nanti.jisuanke.com/t/41294


题意

给出两个点 P P P Q Q Q,以及一些其他点 A 1 A1 A1 A 2 A2 A2 A n An An

要求出一个序列 v 1 v 2 . . . v n v_1v_2...v_n v1v2...vn,使得三角形 △ P Q A v i 包 含 △ P Q A v j ( i &lt; j ) \triangle{PQA{v_i}}包含\triangle{PQA{v_j}}(i&lt;j) PQAviPQAvj(i<j),且 A v j Av_j Avj不在线段 P A v i PAv_i PAvi或者线段 Q A v i QAv_i QAvi上。
问序列 v 1 v 2 . . . v n v_1v_2...v_n v1v2...vn最长是多少,多个最长输出字典序最小的情况。


题解

三角形包含 △ P Q A v i 包 含 △ P Q A v j \triangle{PQA{v_i}}包含\triangle{PQA{v_j}} PQAviPQAvj就表明 ∠ A v i P Q &gt; ∠ A v j P Q \angle{Av_iPQ}&gt;\angle{Av_jPQ} AviPQ>AvjPQ ∠ A v i Q P &gt; ∠ A v j Q P \angle{Av_iQP}&gt;\angle{Av_jQP} AviQP>AvjQP。所以,如果对每个点都把这两个角度求出来作为两个维度,按照其中一个维度排个序,那就是对另一个维度求LIS。
但是求角度或者求cos都会造成精度的丢失,在这里可以用叉积判断,就是要手写个二分。
题目最后是求LIS反过来的字典序最小,那就反过来求一下,推一遍就行了。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> piir;
const int N=2e5+7;
struct Point{
    ll x,y;
    Point operator - (const Point k)const{
        return (Point){x-k.x,y-k.y};
    }
    void input(){scanf("%lld%lld",&x,&y);}
    void output(){printf("%lld%lld",x,y);}
}p,q,a[N];
inline ll cross(const Point k1,const Point k2){
    return k1.x*k2.y-k2.x*k1.y;
}
int T,n,cs;
struct Node{
    int id;
    Point o;
}st[N],b[N],ans[N];
bool cmp1(Node k1,Node k2){
    if(cross(k1.o-p,k2.o-p)==0) return cross(k1.o-q,k2.o-q)>=0;
    return cross(k1.o-p,k2.o-p)>=0;
}
bool cmp2(Node k1,Node k2){
    if(cross(k1.o-p,k2.o-p)==0) return cross(k1.o-q,k2.o-q)<=0;
    return cross(k1.o-p,k2.o-p)<=0;
}
int tp;
int dp[N];
int tot=0;
vector<int>solve1(){
    tp=0;
    int mx=0;
    for(int i=1;i<=tot;i++){
        int lo=1,hi=tp;dp[i]=tp+1;
        while(lo<=hi){
            int mid=lo+hi>>1;
            if(cross(b[i].o-q,st[mid].o-q)<=0) hi=mid-1,dp[i]=mid;
            else lo=mid+1;
        }
        mx=max(dp[i],mx);
        if(dp[i]>tp) st[++tp]=b[i];
        else st[dp[i]]=b[i];
    }
    for(int i=1;i<=mx+1;i++){
        ans[i].id=n+1;
    }
    int now=mx;
    for(int i=tot;i>=1;i--){
        if(dp[i]==mx||cross(b[i].o-q,ans[dp[i]+1].o-q)<0){
            if(dp[i]>=now){
                if(ans[dp[i]].id>b[i].id) now=dp[i],ans[dp[i]]=b[i];
            }
            else if(dp[i]==now-1){
                ans[dp[i]]=b[i];now--;
            }
        }
    }
    vector<int>res;res.clear();
    for(int i=mx;i>=1;i--){
        res.push_back(ans[i].id);
    }
    return res;
}

vector<int>solve2(){
    tp=0;
    int mx=0;
    for(int i=1;i<=tot;i++){
        int lo=1,hi=tp;dp[i]=tp+1;
        while(lo<=hi){
            int mid=lo+hi>>1;
            if(cross(b[i].o-q,st[mid].o-q)>=0) hi=mid-1,dp[i]=mid;
            else lo=mid+1;
        }
        mx=max(dp[i],mx);
        if(dp[i]>tp) st[++tp]=b[i];
        else st[dp[i]]=b[i];
    }
    for(int i=1;i<=mx+1;i++){
        ans[i].id=n+1;
    }
    int now=mx;
    for(int i=tot;i>=1;i--){
        if(dp[i]==mx||cross(b[i].o-q,ans[dp[i]+1].o-q)>0){
            if(dp[i]>=now){
                if(ans[dp[i]].id>b[i].id) now=dp[i],ans[dp[i]]=b[i];
            }
            else if(dp[i]==now-1){
                ans[dp[i]]=b[i];now--;
            }
        }
    }
    vector<int>res;res.clear();
    for(int i=mx;i>=1;i--){
        res.push_back(ans[i].id);
    }
    return res;
}
int main()
{
    //freopen("in.in","r",stdin);
    scanf("%d",&T);
    for(int t=1;t<=T;t++){
        p.input();q.input();
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            a[i].input();
        }

        tot=0;
        for(int i=1;i<=n;i++){
            if(cross(a[i]-p,q-p)<=0){
                tot++;
                b[tot].o=a[i];
                b[tot].id=i;
            }
        }
        sort(b+1,b+1+tot,cmp1);
        vector<int>le=solve1();

        tot=0;
        for(int i=1;i<=n;i++){
            if(cross(a[i]-p,q-p)>=0){
                tot++;
                b[tot].o=a[i];
                b[tot].id=i;
            }
        }
        sort(b+1,b+1+tot,cmp2);
        vector<int>ri=solve2();

        if(le.size()<ri.size()) le=ri;
        else if(le.size()==ri.size()){
            for(int i=0;i<le.size();i++){
                if(le[i]==ri[i]) continue;
                if(le[i]<ri[i]) break;
                le=ri;break;
            }
        }
        printf("Case #%d: %d\n",++cs,le.size());
        for(int i=0;i<le.size();i++){
            printf("%d\n",le[i]);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值