题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6617

题目大意:给出一凸包\(P\),求最小的与\(P\)相似且对应边平行的多边形,使得题目给出的\(m\)个点\(q_i\)都被该多边形包含在内,输出最小相似比

题解:二分答案\(k\),考虑如何判断\(P\)被放大\(k\)倍后是否可以通过平移这\(m\)个点使他们都在多边形内。将多边形的所有边看成有向线段(逆时针),则\(m\)个点都在多边形内当且仅当他们都在这些有向线段的左侧。对第\(i\)条边记录\(f_i\)表示如果点\(q_{f_i}\)在这条边左侧,就能保证所有点都在其左侧,通过对这\(m\)个点求凸包后可以在\(O(m)\)的时间复杂度内求出\(f_i\)。这样我们就只需要知道,是否存在一个平移的向量,使得这\(m\)个点平移之后,所有的点\(q_{f_i}\)都在第\(i\)条边的左侧。

   于是我们如果对每条边\((p_i,p_{i+1})\),求出了符合条件的向量集\(\vec{A_i}\),就可以通过判断他们的交集是否为空来判断当前的比例\(k\)是否合法。可以发现\(\vec{A_i}=\{\vec{OA}|A\text{ is on the left of }\vec{(p_i-q_{f_i},p_{i+1}-q_{f_i})}\}\)代表了一个半平面,这时我们就可以通过求半平面交来解决这道题了。

   注意题目的要求是对应边平行,因此我们还要把原图形旋转\(180^{\circ}\)再做一遍,取两次答案的最小值输出。

#include<bits/stdc++.h>
using namespace std;
#define N 100001
const double eps=1e-10;
int sgn(double x)
{if(x<-eps)return -1;if(x>eps)return 1;return 0;
}
struct Point
{double x,y;void read(){scanf("%lf%lf",&x,&y);}Point operator +(const Point &t)const{return {x+t.x,y+t.y};}Point operator -(const Point &t)const{return {x-t.x,y-t.y};}double operator *(const Point &t)const{return x*t.y-y*t.x;}Point operator *(const double t)const{return {x*t,y*t};}Point operator /(const double t)const{return {x/t,y/t};}double ang()const{return atan2(1.0*y,1.0*x);}double length()const{return sqrt(x*x+y*y);}
}p[N];
struct Line
{Point p1,p2;Point isct(const Line &t)const{double a=(p2-p1)*(t.p1-p1);double b=(p2-p1)*(p1-t.p2);return (t.p1*b+t.p2*a)/(a+b);}
}q[N],line[N];
Point cent;
bool cmpang(const Point &p1,const Point &p2)
{int tmp=sgn((p1-cent).ang()-(p2-cent).ang());if(tmp!=0)return tmp<0;return (p1-cent).length()<(p2-cent).length();
}
struct Polygon
{int n;Point a[N];void read(){scanf("%d",&n);for(int i=1;i<=n;i++)a[i].read();}void ChangetoConvex(){for(int i=2;i<=n;i++)if(a[i].x<a[1].x || (a[i].x==a[1].x && a[i].y<a[1].y))swap(a[1],a[i]);cent=a[1];sort(a+2,a+n+1,cmpang);int top=2;for(int i=3;i<=n;i++){while(top>=2 && (a[top]-a[top-1])*(a[i]-a[top])<=0)top--;a[++top]=a[i];}n=top;}
}P,Q;
int T,nxt[N],pre[N],f[N];
bool check(int i,Point A,Point B)
{return sgn((B-A)*(Q.a[pre[i]]-Q.a[i]))>=0 && sgn((B-A)*(Q.a[nxt[i]]-Q.a[i]))>=0;
}
bool Left(const Point &p,const Line &l){return sgn((l.p2-l.p1)*(p-l.p1))==1;}
bool HalfPlane(int n)
{int h=1,t=1;q[1]=line[1];for(int i=2;i<=n;i++){while(h<t && !Left(p[t-1],line[i]))t--;while(h<t && !Left(p[h],line[i]))h++;if(sgn((q[t].p2-q[t].p1)*(line[i].p2-line[i].p1))==0)q[t]=Left(q[t].p1,line[i])?q[t]:line[i];else q[++t]=line[i];if(h<t)p[t-1]=q[t].isct(q[t-1]);}while(h<t && !Left(p[t-1],q[h]))t--;if(t-h<=1)return false;p[t]=q[t].isct(q[h]);double ans=0;for(int i=h;i<=t;i++)ans+=p[i]*p[(i+1-h)%(t-h+1)+h];return sgn(ans)>=0;
}
bool check(double k)
{for(int i=1;i<=P.n;i++)line[i]={P.a[i]*k-Q.a[f[i]],P.a[i%P.n+1]*k-Q.a[f[i]]};return HalfPlane(P.n);
}
double solve()
{int j=1;for(int i=1;i<=P.n;i++){while(!check(j,P.a[i],P.a[i%P.n+1]))j=nxt[j];f[i]=j;}double l=0,r=1e9,mid;while(l+eps<r){mid=(l+r)*0.5;if(check(mid))r=mid;else l=mid;}return l;
}
void init()
{double ans=1e18;P.read(),Q.read();if(Q.n==1){printf("%.6f\n",0.0);return;}P.ChangetoConvex();Q.ChangetoConvex();for(int i=1;i<=Q.n;i++)nxt[i]=i%Q.n+1,pre[i%Q.n+1]=i;ans=min(ans,solve());for(int i=1;i<=P.n;i++)P.a[i]=P.a[i]*(-1.0);ans=min(ans,solve());printf("%.6f\n",ans);
}
int main()
{scanf("%d",&T);while(T--)init();
}

View Code

   关于\(\vec{A_i}\)的说明:设点\(q_{f_i}\)为\(Q\),则\(\vec{OA}\)满足原点\(O\)在平移\(\vec{OA}\)后,他在有向线段\(\vec{(p_i-Q,p_{i+1}-Q)}\)的左侧。所以原点\(O\)在平移\(\vec{OA}\)后,再平移\(\vec{OQ}\),就能满足其在有向线段\(\vec{(p_i,p_{i+1})}\)的左侧。而\(O+\vec{OA}+\vec{OQ}=O+\vec{OQ}+\vec{OA}=Q+\vec{OA}\),因此将\(Q\)平移\(\vec{OA}\)是能满足要求的。

转载于:https://www.cnblogs.com/DeaphetS/p/11300827.html

[2019HDU多校第四场][HDU 6617][D. Enveloping Convex]相关推荐

  1. 2018 HDU多校第四场赛后补题

    2018 HDU多校第四场赛后补题 自己学校出的毒瘤场..吃枣药丸 hdu中的题号是6332 - 6343. K. Expression in Memories 题意: 判断一个简化版的算术表达式是否 ...

  2. 2019牛客多校第四场 I题 后缀自动机_后缀数组_求两个串de公共子串的种类数

    目录 求若干个串的公共子串个数相关变形题 对一个串建后缀自动机,另一个串在上面跑同时计数 广义后缀自动机 后缀数组 其他:POJ 3415 求两个串长度至少为k的公共子串数量 @(牛客多校第四场 I题 ...

  3. 牛客多校第四场【B-Basic Gcd Problem】

    牛客多校第四场[B-Basic Gcd Problem] 题目链接:https://ac.nowcoder.com/acm/contest/5669/B 思路:先要理解公式,多看几个数据基本就会有点想 ...

  4. hdu 4639 2013多校第四场 hehe Fibonacci 数列,组合计数,字符串处理

    题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4639 题目思路: 首先我们取出所有he这样的东西,考察连续的k个"he"串,通过找 ...

  5. 2014多校第四场1006 || HDU 4902 Nice boat (线段树 区间更新)

    题目链接 题意 : 给你n个初值,然后进行两种操作,第一种操作是将(L,R)这一区间上所有的数变成x,第二种操作是将(L,R)这一区间上所有大于x的数a[i]变成gcd(x,a[i]).输出最后n个数 ...

  6. (2017多校训练第四场)HDU - 6078 Wavel Sequence dp

    传送门:点击打开链接 定义状态dp[i][j][0]表示以a[i],b[j]结尾的且为波谷的情况总和,dp[i][j][1] 为波峰. 对于某个i,j满足a[i] == b[j],则dp[i][j][ ...

  7. 2017多校第3场 HDU 6058 Kanade's sum 双链表,思维

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6058 题外话:这场多校,真心感觉自己的无力,全队都发挥的很差,结束的时候排名掉到了90多,后期没做出字 ...

  8. 多校第六场 HDU 4927 JAVA大数类+模拟

    HDU 4927 题目大意:给定一个长度为n的序列a,每次生成一个新的序列,长度为n-1,新序列b中bi=ai+1−ai,直到序列长度为1.输出最后的数. 思路:这题实在是太晕了,比赛的时候搞了四个小 ...

  9. 2019HDU多校第十场

    (撒花!二十场打完了.虽然题解(riji)咕咕咕了好几场. 1003 Valentine's Day 传送:http://acm.hdu.edu.cn/showproblem.php?pid=6693 ...

最新文章

  1. SCI期刊上发现大量辣眼学术名词,用机翻规避抄袭,作者主要来自中国
  2. Android Studio 单刷《第一行代码》系列 01 —— 第一战 HelloWorld
  3. LINUX Find命令使用
  4. RHEL5 RHEL6 差异 1
  5. Intellij 中的git操作 转!
  6. mysql公告信息管理系统_JSP班级公告管理系统+mysql 班级公告管理系统 - 下载 - 搜珍网...
  7. 解决Linq.ToDictionary()时的键重复问题
  8. ubuntu 18.04安装php 7,如何在Ubuntu 18.04和16.04上安装PHP(7.3,7.2和7.0)?
  9. 用CSS3制作50个超棒动画效果教程
  10. Quill富文本编辑器-图片上传-可编辑图片大小、排版
  11. Flutter开发(十一)—— 五种布局之Sliver滚动布局
  12. [ 物联网篇 ] 27 -使用libcur API 实现本地时间同步的功能,类似NTP功能
  13. canvas+gif.js打造自己的数字雨头像
  14. GNN(图神经网络)在反欺诈领域的落地
  15. IDEA中执行scala程序报错Error:scalac: Error: scala/Function1 java.lang.NoClassDefoundError:Scala/Function1
  16. Markdown详细教程
  17. 用python做一个上位机串口通信_PYTHON制作画加书法源程序
  18. 郑渊洁:一个著作等身的文盲
  19. 一套简单的web即时通讯——第三版
  20. jyh.project

热门文章

  1. Halide学习笔记----Halide tutorial源码阅读5
  2. 详解康托展开与逆康托展开
  3. 中级宏观经济学复习范围(马工程)
  4. 【目标跟踪】|单目标跟踪指标
  5. 3年自动化测试,我突然想转测试开发了,开发测试工具平台......
  6. MySQL+Java 图书管理系统
  7. 为什么移动计算比移动数据更便宜?
  8. STM32学习(二)
  9. 在红帽linux创建目录,redhat linux建文件系统
  10. C语言中 *x++ (*x)++ ++*x *++x的区别