[HNOI2007]最小矩形覆盖

 坑点:如果输出值的绝对值小于 1e-5 那么结果可能输出 -0.00000 ,需要特判一下。

#include <bits/stdc++.h>
#define ne(i) p[(i+1)%n]
using namespace std;
typedef long long LL;
typedef int lint;
const int maxn = 50001;
const double eps = 1e-12;
const double PI = acos(-1.0);
int sgn(double x)
{
    if(fabs(x) < eps)return 0;
    if(x < 0)return -1;
    else return 1;
}
struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y)
    { x = _x;y = _y;
    }
    Point operator -(const Point &b)const
    {
        return Point(x - b.x,y - b.y);
    }
//叉积
    double operator ^(const Point &b)const
    {
        return x*b.y - y*b.x; }
//点积
    double operator *(const Point &b)const
    {
        return x*b.x + y*b.y; }
//绕原点旋转角度B(弧度值),后x,y的变化
    void transXY(double B)
    {
        double tx = x,ty = y; x = tx*cos(B) - ty*sin(B);
        y = tx*sin(B) + ty*cos(B);
    }
};
struct Line
{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e)
    { s = _s;e = _e;
    }
    pair<int,Point> operator &(const Line &b)const
    {
        Point res = s;
        if(sgn((s-e)^(b.s-b.e)) == 0)
        {
            if(sgn((s-b.e)^(b.s-b.e)) == 0)
                return make_pair(0,res);//重合
            else return make_pair(1,res);//平行
        }
        double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
        res.x += (e.x-s.x)*t;
        res.y += (e.y-s.y)*t;
        return make_pair(2,res);
    }
};
Point PointToLine(Point P,Line L)
{
    Point result;
    double t = ((P-L.s)*(L.e-L.s))/((L.e-L.s)*(L.e-L.s));
    result.x = L.s.x + (L.e.x-L.s.x)*t;
    result.y = L.s.y + (L.e.y-L.s.y)*t;
    return result;
}
double dist(Point a,Point b)
{
    return sqrt((a-b)*(a-b));
}
int dist2(Point a,Point b)
{
    return (a-b)*(a-b);
}
Point lis[maxn];
int Stack[maxn],top;
//相对于list[0]的极角排序

bool _cmp(Point p1,Point p2)
{
    double tmp = (p1-lis[0])^(p2-lis[0]);
    if(sgn(tmp) > 0)return true;
    else if(sgn(tmp) == 0 && sgn(dist(p1,lis[0]) - dist(p2,lis[0])) <= 0)
        return true;
    else return false;
}
void Graham(int n)
{
    Point p0;
    int k = 0;
    p0 = lis[0];
//找最下边的一个点
    for(int i = 1;i < n;i++)
    {
        if( (p0.y > lis[i].y) || (p0.y == lis[i].y && p0.x > lis[i].x) )
        {
            p0 = lis[i];
            k = i;
        }
    }
    swap(lis[k],lis[0]);
    sort(lis+1,lis+n,_cmp);
    if(n == 1)
    {
        top = 1;
        Stack[0] = 0;
        return; }
    if(n == 2)
    {
        top = 2;
        Stack[0] = 0;
        Stack[1] = 1;
        return ; }
    Stack[0] = 0;
    Stack[1] = 1;
    top = 2;
    for(int i = 2;i < n;i++)
    {
        while(top > 1 &&sgn((lis[Stack[top-1]]-lis[Stack[top-2]])^(lis[i]-lis[Stack[top-2]])) <=0)
            top--;
        Stack[top++] = i;
    }
}
double ans = 1<<30;
Point ans1,ans2,ans3,ans4;
void rotating_calipers(Point p[],int n)
{
    lint cur = 1;
    lint cur1 = 1;
    lint cur2 = 1;
    lint f = 1;
    for( lint i = 0;i < n;i++ ){
        while( sgn( (ne(cur)-p[cur])^(ne(i)-p[i]) ) <= 0 ) cur  = (cur + 1)%n;
        while( sgn( (ne(cur1)-p[cur1])*( ne(i)-p[i] ) ) >= 0 ) cur1 = (cur1+1)%n;
        if(f){cur2 = cur;f = 0;}
        while( sgn( (ne(cur2)-p[cur2])*( ne(i)-p[i] ) < 0 ) ) cur2 = (cur2+1)%n;
        double d = dist( p[i],ne(i) );
        double s = d + abs((ne(i)-p[i])*(p[cur1]-ne(i))) /d + abs((ne(i)-p[i])*(p[cur2]-p[i])) /  d;
        Point x = PointToLine( p[cur],Line( ne(i),p[i] ) );
        double res = dist( x,p[cur] )*s ;
        if( res < ans ){
            ans = res;
            ans1 = PointToLine(p[cur2],Line( ne(i),p[i] ));
            ans2 = PointToLine(p[cur1],Line(ne(i),p[i]));
            ans3 = PointToLine(p[cur],Line(p[cur1],ans2));
            ans4 = PointToLine( p[cur],Line( ans1,p[cur2] ) );
        }
    }
    return;
}
Point p[maxn];
struct point{
    double x,y;
    lint id;
    point( double xx = 0,double yy = 0,lint iidd = 0 ){
        x = xx;
        y = yy;
        id = iidd;
    }
    bool operator < ( const point& b )const{
        if( y != b.y )
            return y < b.y;
        else return x < b.x;
    }
};
point pp[4];
lint search( ){
    pp[0] = point( ans1.x,ans1.y,1 );
    pp[1] = point( ans2.x,ans2.y,2 );
    pp[2] = point( ans3.x,ans3.y,3 );
    pp[3] = point( ans4.x,ans4.y,4 );
    sort( pp,pp+4 );
    return pp[0].id;
}
int main(){
    lint n;
    double x,y;
    lint tot = 0;
    scanf("%d",&n);
    for( lint i = 1;i <= n;i++ ){
        scanf("%lf%lf",&x,&y);
        lis[tot++] = Point( x,y );
    }
    Graham(n);
    for( lint i = 0;i < top;i++ ){
        p[i] = lis[Stack[i]];
    }
    rotating_calipers( p,top );
    if( abs( ans ) < 1e-5 ) ans = 0;
    printf("%.5lf\n",ans);
    lint id = search();
    // fflush(stdout);
    for( lint i = id,cnt = 0;cnt < 4;cnt++  ){
        if( i == 1 ){
            if( abs( ans1.x ) < 1e-5 ) ans1.x = 0;
            if( abs( ans1.y ) < 1e-5 ) ans1.y = 0;
            printf("%.5lf %.5lf\n",ans1.x,ans1.y);
        }else if( i == 2 ){
            if( abs( ans2.x ) < 1e-5 ) ans2.x = 0;
            if( abs( ans2.y ) < 1e-5 ) ans2.y = 0;
            printf("%.5lf %.5lf\n",ans2.x,ans2.y);
        }else if( i == 3 ){
            if( abs( ans3.x ) < 1e-5 ) ans3.x = 0;
            if( abs( ans3.y ) < 1e-5 ) ans3.y = 0;
            printf("%.5lf %.5lf\n",ans3.x,ans3.y);
        }else{
            if( abs( ans4.x ) < 1e-5 ) ans4.x = 0;
            if( abs( ans4.y ) < 1e-5 ) ans4.y = 0;
            printf("%.5lf %.5lf\n",ans4.x,ans4.y);
        }
        i= i%4+1;
        //fflush(stdout);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值