洛谷2600 ZJOI2008瞭望塔(半平面交+推柿子)

qwq有点自闭
我果然还是好菜啊
根本想不出来题了

首先,我们来看这个题。我们从左到右考虑每个点。对于一个点来说,他和他下一个点组成一个斜坡,只能被他分开的两个半平面中,左边的那个看到。

那么根据原图的点,我们可以构造出 n − 1 n-1 n1段有向直线,然后对这些直线求一个半平面交就能知道有效区域了。

qwq可是应该怎么求解呢?

首先一个比较显然的思路的话,我们对于每一个半平面交的顶点,然后求出他在底部的直线中的对应的位置以及横坐标等于他的直线上的点的纵坐标是多少。用他的纵坐标减去对应的纵坐标就是高度了

求解析式的话,qwq我是用了一些奇奇怪怪的办法,只求 k k k,然后用 x x x端点的 y y y值加上横坐标之差乘上 k k k o k ok ok

 for (int i=1;i<=num;i++)
       for (int j=1;j<m;j++)
         if (dcmp(ymh[i].x-a[j].x)>=0 && 0<=dcmp(a[j+1].x-ymh[i].x))
         {
            double k = (a[j+1].y-a[j].y)/(a[j+1].x-a[j].x);
            ans = min(ymh[i].y-(ymh[i].x-a[j].x)*k-a[j].y,ans); 
         }

那是不是这样就结束了呢????

貌似不是的

我们只考虑了上面可能存在的最优解的端点,却并没有考虑下面的,一个比较容易发现的结论是,如果存在一段斜坡的贡献没有考虑到,一定是会是在拐点处(手玩几个栗子就能看出来qwq),那么我们不妨找出所有横坐标是给定区间的下部拐点,然后用类似的方法计算贡献

for (int i=1;i<=m;i++)
        for (int j=1;j<num;j++)
        {
           if (dcmp(a[i].x-ymh[j].x)>=0 && (a[i].x-ymh[j+1].x)<=0)
           {
              double k = (ymh[j+1].y-ymh[j].y)/(ymh[j+1].x-ymh[j].x);
              ans = min((a[i].x-ymh[j].x)*k+ymh[j].y - a[i].y,ans);
           }
        }

下面是hi整个题目的代码

需要注意的是,还是要加入一些边界来限制半平面交不是无限大的。

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 2e5+1e2;
const double pai = acos(-1);
const double eps = 1e-8;
const double inf = 1e12;
int dcmp(double x)
{
    if (x>eps) return 1;
    if (x<-eps) return -1;
    return 0;
}
struct Point
{
    double x,y;
};
Point operator + (Point a,Point b)
{
    return (Point){a.x+b.x,a.y+b.y};
}
Point operator - (Point a,Point b)
{
    return (Point){a.x-b.x,a.y-b.y};
}
Point operator * (Point a,double b)
{
    return (Point){a.x*b,a.y*b};
}
Point operator / (Point a,double b)
{
    return (Point){a.x/b,a.y/b};
}
struct Line
{
   Point x,y;
   double ang;	
};
Line count(Point a,Point b)
{
    Point lyf = b-a;
    return (Line){a,b,atan2(lyf.y,lyf.x)};
}
double diancheng(Point x,Point y)
{
    return x.x*y.x+x.y*y.y;
}
double chacheng(Point x,Point y)
{
    return x.x*y.y-x.y*y.x;
}
int pingxing(Line x,Line y)
{
    return dcmp(chacheng(x.y-x.x,y.y-y.x))==0;
}
Point jiaodian(Line a,Line b)
{
    return a.x+(a.y-a.x)*(chacheng(b.y-b.x,a.x-b.x)/chacheng(a.y-a.x,b.y-b.x));
}
bool isright(Line a,Point b)
{
    return dcmp(chacheng(a.y-a.x,b-a.x))<=0;
}
bool operator < (Line a,Line b)
{
    double now = a.ang-b.ang;
    if (dcmp(now)!=0)
    {
        if (dcmp(now)==-1) return 1;
        else return 0;
    }
    else
    {
        double tmp = chacheng(a.y-a.x,b.y-a.x);
        if (dcmp(tmp)==-1) return 1;
        else return 0;
    }
}
int n,m;
Point qd[maxn];
Line qx[maxn];
Point ymh[maxn];
Point a[maxn];
int head=0,tail=1;
int num;
Line l[maxn];
bool solve()
{
   sort(l+1,l+1+n);
   qx[++head]=l[1];
   for (int i=2;i<=n;i++)
   {
     if (dcmp(l[i].ang-l[i-1].ang)==0) continue;
     if (head<tail && (pingxing(qx[head],qx[head+1]) || pingxing(qx[tail],qx[tail-1]))) return false;
     while (head<tail && isright(l[i],qd[tail-1])) tail--;
     while (head<tail && isright(l[i],qd[head])) head++;
     qx[++tail]=l[i];
     if(head<tail) qd[tail-1]=jiaodian(qx[tail],qx[tail-1]);
   }
   while (head<tail && isright(qx[head],qd[tail-1])) tail--;
   while (head<tail && isright(qx[tail],qd[head])) head++;
   if (tail-head+1<=2) return false;
   qd[tail]=jiaodian(qx[head],qx[tail]);
   for (int i=head;i<=tail;i++) ymh[++num]=qd[i];
   return true; 
} 
bool cmp(Point x,Point y)
{
  if (x.x==y.x) return x.y<y.y;
  return x.x<y.x;
}
int main()
{
  m=read();
  for (int i=1;i<=m;i++) scanf("%lf",&a[i].x);
  for (int i=1;i<=m;i++) scanf("%lf",&a[i].y);  
  sort(a+1,a+1+m,cmp);
  for (int i=1;i<=m-1;i++) 
  {
    l[++n]=count(a[i],a[i+1]);
  }
  Point aa = (Point){0,0};
  Point b = (Point){inf,0};
  Point c = (Point){inf,inf};
  Point d = (Point){0,inf};
  l[++n]=count(aa,b);
  l[++n]=count(b,c);
  l[++n]=count(c,d);
  l[++n]=count(d,aa);
  double ans = 1e18;
  if (solve())
  {
     for (int i=1;i<=num;i++)
       for (int j=1;j<m;j++)
         if (dcmp(ymh[i].x-a[j].x)>=0 && 0<=dcmp(a[j+1].x-ymh[i].x))
         {
            double k = (a[j+1].y-a[j].y)/(a[j+1].x-a[j].x);
            ans = min(ymh[i].y-(ymh[i].x-a[j].x)*k-a[j].y,ans); 
         }
      for (int i=1;i<=m;i++)
        for (int j=1;j<num;j++)
        {
           if (dcmp(a[i].x-ymh[j].x)>=0 && (a[i].x-ymh[j+1].x)<=0)
           {
              double k = (ymh[j+1].y-ymh[j].y)/(ymh[j+1].x-ymh[j].x);
              ans = min((a[i].x-ymh[j].x)*k+ymh[j].y - a[i].y,ans);
           }
        }
  }
  else
  {
     cout<<"scb is our red sun!";
     return 0;
  }
  printf("%.3lf\n",ans);
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值