官方题解,内附我不会的\(O(nlogn)\)做法
http://www.csc.kth.se/~austrin/icpc/finals2018solutions.pdf
这题题意很简单.

给你一个可以不凸的多边形,要求找一个点距离所有多边形的顶点最小距离最大.

首先这个东西很像是半平面交,因为半平面交可以做

给你一个可以不凸的多边形,要求找一个点距离所有多边形的边最小距离最大.

然而直接上半平面交是搞不出来的.
观察一下性质,显然最优取值点在某一点对的垂直平分线上.

然后大力
https://en.wikipedia.org/wiki/Voronoi_diagram
一发.
发现自己不会写找出维诺图的\(O(nlogn)\)算法.
这时候就有两种思考:
1.乱搞,卡精度
2.观察到数据范围很小,直接暴力半平面交找出维诺图的顶点.(然而我并没有想到这个)

考虑乱搞,怎么乱搞呢,首先要利用结论,垂直平分线上的结论.
那么我随机生成一个点,让他随机沿垂直平分线走一个随机的距离.实践证明,这样做很不理想.
考虑抱精度,那么设一个步长,每次乘上一个alpha,搞一搞精度.
还是不能过.

因为点会走到维诺图外,所以只沿维诺图的某条边所在直线走,不一定可以走到维诺图的最优定点上.
考虑随机一个角度,多随机几次,在那个角度上走步长.

然后发现被近乎一条直线的多边形卡爆了,那么考虑再加一种策略,向一个随机顶点走.
然后还是被卡爆了.

考虑借鉴遗传算法的思想,在下一次的方向中借鉴上一次的较优解.
然后就勉强可以水过.

#include <bits/stdc++.h>using namespace std;
template<class T>
bool Chkmin(T &x,T y){return x>y?(x=y,1):0;
}
template<class T>
bool Chkmax(T &x,T y){return x<y?(x=y,1):0;
}
template<class T>
T sqr(T x){return x*x;
}
typedef double ld;
typedef long long i64;
const ld INF=1e18;
const ld EPS=1e-8;
struct point{ld x,y;point operator +(const point &_) const{return {x+_.x,y+_.y};}point operator -(const point &_) const{return {x-_.x,y-_.y};}point operator /(const ld &_){return {x/_,y/_};}point operator *(const ld &_){return {x*_,y*_};}ld cross(const point &_) const{return x*_.y-y*_.x;}ld dot(const point &_) const{return x*_.x+y*_.y;}point rotate() const{return {-y,x};}ld dis(){return sqrt(sqr(x)+sqr(y));}
};
ostream& operator <<(ostream & os,const point &_){return os<<"{"<<_.x<<","<<_.y<<"}";
}
struct segment{point s,t;bool strict_cross(const segment &_) const{return (t-s).cross(_.s-s)*(t-s).cross(_.t-s)<0&&(_.t-_.s).cross(s-_.s)*(_.t-_.s).cross(t-_.s)<0;}bool operator &(const segment &_) const{//cerr<<"&"<<s<<" "<<t<<",,"<<_.s<<" "<<_.t<<endl;return strict_cross(_);}
};
struct polygon{vector<point> data;size_t next(size_t val) const{return val==size()-1?0:val+1;}size_t size() const{return data.size();}segment edge(int x) const{return segment({data[x],data[next(x)]});}point& operator [](const size_t &x){return data[x];}
};
bool in(const point &test,const polygon &con){static const point add({42348.23712341,34435.61431236});int counter=0;for (int i=0; i<con.size(); ++i)if (segment({test,test+add})&con.edge(i)) ++counter;return counter&1;
}
point neareast(const point &test,polygon &con){ld nowdis=(test-con[0]).dis();point rvalue=con[0];for (size_t i=1; i<con.size(); ++i)if (Chkmin(nowdis,(test-con[i]).dis())) rvalue=con[i];return rvalue;
}
const int C=10000;
const int T=1000;
point rec[T];
bool b[T];
ld Rand(){return (((i64)rand()<<31)+rand());
}
int mxx,mix,mxy,miy;
int RandIntX(){return rand()%(mxx-mix+1)+mix;
}
int RandIntY(){return rand()%(mxy-miy+1)+miy;
}
polygon con;
point trans(ld x,point y){return y*(x/y.dis());
}
point pp(){int x,y;do{x=rand()%con.size();y=rand()%con.size();}while (x==y);return (con[x]-con[y]).rotate();
}
point conclude(int TT){point x({RandIntX(),RandIntY()});//bug herebool fi=1;for (ld step=C/2; step>EPS; step*=0.8){//cerr<<x<<" "<<step<<endl;rec[1]=x+trans(step,x-rec[0]);rec[0]=x;if (fi)for (int i=2,t; i<TT; ++i){if ((t=rand())&1){rec[i]=x+trans(step,pp());//optimize}else if ((t>>1)&1) rec[i]=x+trans(step,(con[rand()%con.size()]-x));else{ld alpha=Rand();rec[i]=x+point({step*cos(alpha),step*sin(alpha)});}}elsefor (int i=2,t; i<TT; ++i)if ((t=rand())&1){ld alpha=Rand();rec[i]=x+point({step*cos(alpha),step*sin(alpha)});}else if ((t>>1)&1) rec[i]=x+trans(step,(con[rand()%con.size()]-x));else{rec[i]=x+trans(step,pp());//optimize}int counter=0;for (int i=0; i<TT; ++i)if (in(rec[i],con)){//bug//cerr<<"inint"<<i<<" "<<rec[i]<<endl;if (fi){//cerr<<x<<endl;fi=0;step=C/2;}b[i]=1;++counter;}else b[i]=0;//getchar();//exit(0);if (counter){ld nowdis=-1;for (int i=0; i<TT; ++i)if (b[i]&&Chkmax(nowdis,(rec[i]-neareast(rec[i],con)).dis())){//cerr<<"UPD"<<nowdis<<endl;x=rec[i];}}else{ld nowdis=INF;for (int i=0; i<TT; ++i)if (Chkmin(nowdis,(rec[i]-neareast(rec[i],con)).dis())){x=rec[i];}}}return x;
}
int main(){int n;cin>>n;con.data.resize(n);mxx=-10000;mix=10000;mxy=-10000;miy=10000;for (int i=0; i<n; ++i){int x,y;cin>>x>>y;con[i]=point({x,y});mix=min(mix,x);mxx=max(mxx,x);miy=min(miy,y);mxy=max(mxy,y);}int TIME=clock();ld ans=0;while (clock()-TIME<CLOCKS_PER_SEC){int p=rand()%15+5;//cerr<<"P"<<p<<endl;point tmp=conclude(p);ans=max(ans,(tmp-neareast(tmp,con)).dis());}while (clock()-TIME<CLOCKS_PER_SEC*1.5){int p=rand()%200;if (p>100) p=rand()%200;if (p>100) p=rand()%200;if (p>100) p=rand()%200;if (p>100) p=rand()%200;p=max(p,30);point tmp=conclude(p);ans=max(ans,(tmp-neareast(tmp,con)).dis());//cerr<<"amns"<<ans<<endl;}cout<<fixed<<setprecision(10)<<ans;
}

考虑正经算法.
半平面交怎么求维诺图呢?
把某一个顶点作为线段端点的垂直平分线搞出来,然后暴力半平面交,最后的点集就是维诺图上在该点附近的顶点.
结论:维诺图的顶点数和边数都为\(O(n)\)级别.
注意维诺图的边和多边形的交点也有可能是答案.
复杂度\(O(n^2logn)\)

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optmize(3)
#include <bits/stdc++.h>
using namespace std;
template<class T>
bool Chkmin(T &x,T y){return x>y?(x=y,1):0;
}
template<class T>
bool Chkmax(T &x,T y){return x<y?(x=y,1):0;
}
template<class T>
T sqr(T x){return x*x;
}
typedef double ld;
typedef long long i64;
const ld INF=1e18;
const ld EPS=1e-9;
struct point{ld x,y;point operator +(const point &_) const{return {x+_.x,y+_.y};}point operator -(const point &_) const{return {x-_.x,y-_.y};}point operator /(const ld &_) const{return {x/_,y/_};}point operator *(const ld &_) const{return {x*_,y*_};}ld cross(const point &_) const{return x*_.y-y*_.x;}ld dot(const point &_) const{return x*_.x+y*_.y;}bool operator ==(const point &_) const{return x==_.x&&y==_.y;}bool operator !=(const point &_) const{return x!=_.x||y!=_.y;}point rotate() const{return {-y,x};}ld dis() const{return sqrt(sqr(x)+sqr(y));}ld sqrdis() const{return x*x+y*y;}point makelong(ld len=3e4) const{ld t=len/(dis());return (*this)*t;}
};
ld atan2(const point &x){return atan2(x.y,x.x);
}
ostream& operator <<(ostream & os,const point &_){return os<<"{"<<_.x<<","<<_.y<<"}";
}
struct segment{point s,t;bool strict_cross(const segment &_) const{return (t-s).cross(_.s-s)*(t-s).cross(_.t-s)<0&&(_.t-_.s).cross(s-_.s)*(_.t-_.s).cross(t-_.s)<0;}bool operator &(const segment &_) const{//cerr<<"&"<<s<<" "<<t<<",,"<<_.s<<" "<<_.t<<endl;return strict_cross(_);}bool onright(const point &_) const{return (_-s).cross(t-s)>0;}point generator(const ld &_) const{return s+(t-s)*_;}point intersect(const segment &_) const{ld t1=(t-s).cross(_.t-_.s);ld t2=(_.t-_.s).cross(s-_.s);return generator(t2/t1);}bool on(const point &_) const{//cerr<<(_-s).dot(_-t)<<" "<<fabs((_-s).cross(_-t))<<endl;return (_-s).dot(_-t)<0&&fabs((_-s).cross(_-t))<EPS;}bool line_on(const point &_) const{//cerr<<(_-s).dot(_-t)<<" "<<fabs((_-s).cross(_-t))<<endl;return (_-s).dot(_-t)<0;}
};
ld atan2(const segment &x){return atan2(x.t-x.s);
}
ld ans;
struct polygon:vector<point>{size_t next(size_t val) const{return val==size()-1?0:val+1;}segment edge(int x) const{return segment({(*this)[x],(*this)[next(x)]});}ld neareast(const point &p) const{//cerr<<"ne"<<p<<endl;ld ret=INF;ld c=ans*ans;for (auto i:*this)if ((ret=min(ret,(i-p).sqrdis()))<=c) break;//if (sqrt(ret)>30000) cerr<<"SUCC"<<p<<" "<<sqrt(ret)<<endl;return sqrt(ret);}bool in(const point &p) const{for (size_t i=0; i<this->size(); ++i){auto j=edge(i);if (j.on(p)) return 1;}static const point v={43333.3435499546546L,74354.4534534512434L};int ret=0;for (size_t i=0; i<this->size(); ++i){auto j=edge(i);ret+=segment{p,p+v}&j;}return ret&1;}
};
typedef vector<segment> vs;
polygon con;
struct Segment:segment{ld tt;Segment(){}Segment(const segment &x):segment(x){tt=atan2(x);}
};
polygon halfplaneintersect(vs v){vector<Segment> vp(v.begin(),v.end());stable_sort(vp.begin(),vp.end(),[](const Segment &x,const Segment &y)->bool{if (x.tt+EPS<y.tt) return 1;if (y.tt+EPS<x.tt) return 0;return x.onright(y.s);});//for (auto i:v) cerr<<i.s<<" "<<i.t<<endl;//exit(0);static const int N=2005;static Segment q[N];static point p[N];int l=0,r=-1;for (int i=0; i<vp.size(); ++i){//cerr<<"Line: "<<"\t"<<v[i].s<<" "<<v[i].t<<" "<<atan2(v[i])<<endl;while (l<r&&vp[i].onright(p[r])) --r;while (l<r&&vp[i].onright(p[l+1])) ++l;if (l<=r&&vp[i].tt<q[r].tt+EPS) continue;q[++r]=vp[i];if (l<r){//cerr<<"intersect \t \t \t"<<l<<" "<<r<<" "<<p[r]<<endl;p[r]=q[r-1].intersect(q[r]);}//cerr<<"this"<<l<<" "<<r<<endl;}while (l<r&&q[l].onright(p[r])) --r;while (l<r&&q[r].onright(p[l+1])) ++l;//cerr<<"atlast \t"<<l<<" "<<r<<endl;p[l]=q[l].intersect(q[r]);polygon ret;ret.assign(p+l,p+r+1);for (int i=l; i<=r; ++i){segment t1=q[i];for (int j=0; j<con.size(); ++j){segment t2=con.edge(j);point tmp=t1.intersect(t2);//cerr<<"tmp"<<tmp<<" "<<t1.s<<" "<<t1.t<<" "<<t1.on(tmp)<<endl;//cerr<<"init"<<endl;if (t2.line_on(tmp))ans=max(ans,con.neareast(tmp));}}//for (auto i:ret) cerr<<i<<"|";//cerr<<endl;return ret;
}
segment perpendicular_bisector(point x,point y){point a=(x+y)/2,b=(y-x).rotate().makelong();return {a-b,a+b};
}
void solve(polygon result){//cerr<<"RE"<<result.size()<<endl;for (auto i:result){//cerr<<i<<endl;if (con.in(i)){//cerr<<"I"<<i<<endl;ans=max(ans,con.neareast(i));}}
}
void test(){segment a2({{0,0},{1,1}});segment a1({{1,0},{-1,0}});cerr<<a1.intersect(a2)<<endl;
}
int main(){//test();int n;cin>>n;con.resize(n);for (int i=0; i<n; ++i){int x,y;cin>>x>>y;con[i]={x,y};}for (auto i:con){//cerr<<"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"<<i<<endl;vs tmp;for (auto j:con)if (i!=j){tmp.push_back(perpendicular_bisector(i,j));//cerr<<tmp.back().s<<" "<<tmp.back().t<<" "<<j<<endl;}solve(halfplaneintersect(tmp));//return 0;}cout<<fixed<<setprecision(10)<<ans;
}

转载于:https://www.cnblogs.com/Yuhuger/p/10738412.html

LOJ 6409. 「ICPC World Finals 2018」熊猫保护区相关推荐

  1. 【LOJ6405】「ICPC World Finals 2018」征服世界

    [题目链接] 点击打开链接 [思路要点] 建议参考 W C 2019 WC2019 WC2019 第一课堂陈江伦的<模拟费用流问题>课件. 我们称需要军队的地方为老鼠,军队为洞,那么我们可 ...

  2. [TJOI2013]拯救小矮人(反悔贪心证明),「ICPC World Finals 2019」Hobson 的火车(基环树,差分)

    2021-09-07 test [TJOI2013]拯救小矮人 「ICPC World Finals 2019」Hobson 的火车 [TJOI2013]拯救小矮人 luogu4823 考试题目的数据 ...

  3. 「ICPC World Finals 2019」何以伊名始(广义后缀自动机)

    题目链接 题解: 对输入的trie图建立SAM,然后将询问反转,这个串对应的点的endpos集合就是答案. 还挺水的(逃 AC代码: #pragma GCC optimize(2) #include& ...

  4. Loj #6503. 「雅礼集训 2018 Day4」Magic

    Loj #6503. 「雅礼集训 2018 Day4」Magic 题目描述 前进!前进!不择手段地前进!--托马斯 · 维德 魔法纪元元年. 1453 年 5 月 3 日 16 时,高维碎片接触地球. ...

  5. [LOJ]#6515. 「雅礼集训 2018 Day10」贪玩蓝月

    Solution 离线做法很简单,就是线段树分治,不过复杂度是 q m o d log ⁡ qmod\log qmodlog. 考虑在线做法,在线段树分治中,我们并没有利用到删除以及加入都只会在两端进 ...

  6. LOJ #510. 「LibreOJ NOI Round #1」北校门外的回忆(倍增+动态开点线段树)

    题目 这个题是一个精彩的分析性质区间离散的问题 真的详细 维护链真的一绝. LOJ\rm LOJLOJ最短ACCode\rm AC \ CodeAC Code #include<bits/std ...

  7. LOJ#510. 「LibreOJ NOI Round #1」北校门外的回忆(线段树)

    题面 传送门 题解 感谢\(@M\_sea\)的代码我总算看懂题解了-- 这个操作的本质就是每次把\(x\)的\(k\)进制最低位乘\(2\)并进位,根据基本同余芝士如果\(k\)是奇数那么最低位永远 ...

  8. LOJ #573. 「LibreOJ NOI Round #2」单枪匹马 线段树

    $f$ 函数暴力计算的话是 $O(n)$ 的(用一个 $\frac{x}{y}$ 来保存每一步计算结果,然后依次合并) 我们将一段区间的结果写成 $\frac{ax+by}{cx+dy}$ 的形式,初 ...

  9. LOJ#538. 「LibreOJ NOIP Round #1」数列递推

    description sosusosu 虐爆 OI 之后成为了一名文化课选手.一天,他做作业碰到了一堆数列问题,每道题给出的数列都是以下形式: 给定一个下标从000开始,无限长的整数列ai{a_{i ...

  10. LOJ#539. 「LibreOJ NOIP Round #1」旅游路线

    n<=100,m<=1000的图,在此图上用油箱容量C<=1e5的车来旅行,旅行时,走一条边会耗一单伟油,在点i时,若油量<ci,则可以把油以pi的价格补到ci,pi<= ...

最新文章

  1. OpenInventor开发笔记:解决FaceSet的填充问题
  2. html5 progress css,CSS content: attr() on HTML5 progress doesn't work
  3. 微信公众平台开发(97) 图文消息
  4. 【数据仓库】OLTP系统和OLAP系统区别
  5. 在线HTTP请求/响应头转JSON工具
  6. OSPF(Open Shortest Path First)开放式最短路径优先协议05
  7. GCP+WORDPRESS建站。
  8. 永中word页码怎么从第二页开始_如何在Word的任意一页插入页码?原来还有这么简单的方法...
  9. 关于 虚拟机交叉编译目标机程序,使用filezilla的ftp方式传输到板子上没问题,后来同样环境传入应用运行宕机 的解决方法
  10. Windows API大全
  11. What is tethering and how do you enable tethering?
  12. jpg转bmpbmp转jpg
  13. 小程序与php 实现微信支付
  14. Android系统OTA升级
  15. Android11 HAL层开发
  16. attrib批量显示文件夹_怎样批量修改文件夹或文件的系统隐藏属性
  17. 网络编程 socket函数参数介绍
  18. 阳光智博冲刺港股上市:超九成收入依赖阳光城,毛利率降至32%
  19. I3S向3DTiles数据格式的转换
  20. 重力传感器(二)——屏幕旋转OrientationEventListener监听

热门文章

  1. Python服务器管理模块psutil学习使用
  2. sublime text编辑器修改python代码的缩进设风格
  3. PriorityQueue优先队列
  4. 2021-06-26 严格检查模式 字符串
  5. 以太坊虚拟机 EVM(4)分布式存储架构设计(FISCO BCOS为例)
  6. 数据库学习笔记3-隔离级别 Read Uncommitted
  7. linux syslog 删除文件_恢复日志文件syslog在Linux中删除的方法
  8. 域名访问html乱码,显示地址和域名解析时出现乱码
  9. JDBC12 ORM01 Object[]存放一条记录
  10. 锐捷服务器有什么型号,锐捷管理服务器RG-RCMS-16