BZOJ4739 : 定向越野
起点/终点向每个圆的切点连边。
任意两个圆的公切点之间连边。
同一圆上相邻两个关键点之间连边。
然后Dijkstra求最短路即可,时间复杂度$O(n^3)$。
注意判边可行性的时候要忽略这条边来源的圆,可以提高精度。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int N=510,M=1100000;
const double eps=1e-6,PI=acos(-1.0);
inline double sqr(double x){return x*x;}
struct P{
double x,y;
P(){x=y=0;}
P(double _x,double _y){x=_x,y=_y;}
P operator+(const P&v)const{return P(x+v.x,y+v.y);}
P operator-(const P&v)const{return P(x-v.x,y-v.y);}
P operator*(double v)const{return P(x*v,y*v);}
P operator/(double v)const{return P(x/v,y/v);}
double operator*(const P&v){return x*v.x+y*v.y;}
double len(){return hypot(x,y);}
double len_sqr(){return x*x+y*y;}
P rotate(double c)const{return P(x*cos(c)-y*sin(c),x*sin(c)+y*cos(c));}
P trunc(double l){return(*this)*l/len();}
void read(){scanf("%lf%lf",&x,&y);}
}a[M];
int n,cnt,i,j,cp[N],pool[N][2205];double w[M];
inline bool cmp(int x,int y){return w[x]<w[y];}
inline double cross(const P&a,const P&b){return a.x*b.y-a.y*b.x;}
struct Cir{
P c;double r,rr;
void read(){c.read();scanf("%lf",&r);rr=sqr(r);}
P point(double a)const{return P(c.x+r*cos(a),c.y+r*sin(a));}
bool intersection(const P&a,const P&b){
if((c-a)*(b-a)>-eps&&(c-b)*(a-b)>-eps)return sqr(cross(c-a,b-a))-rr*(b-a).len_sqr()<-eps;
if((c-a).len_sqr()-rr<-eps)return 1;
return (c-b).len_sqr()-rr<-eps;
}
}b[N];
namespace G{
const int MAXE=M*3;
int g[M],v[MAXE],nxt[MAXE],ed;double w[MAXE],d[M];
typedef pair<double,int>P;
priority_queue<P,vector<P>,greater<P> >q;
inline void add(int x,int y,double z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;}
inline void ext(int x,double y){if(y+eps<d[x])q.push(P(d[x]=y,x));}
double solve(){
for(i=1;i<=cnt;i++)d[i]=1e9;
ext(1,0);
while(!q.empty()){
P t=q.top();q.pop();
if(d[t.second]+eps<t.first)continue;
for(i=g[t.second];i;i=nxt[i])ext(v[i],t.first+w[i]);
}
return d[2];
}
}
inline void add(int x,int y,int z,int u=0,int v=0){
for(int i=1;i<=n;i++)if(i!=u&&i!=v)if(b[i].intersection(a[x],a[y]))return;
double t=(a[x]-a[y]).len();
G::add(x,y,t);
if(z==2)G::add(y,x,t);
}
inline void getTangents(const P&p,const Cir&C,int v){
P u=C.c-p;
double dist=u.len(),ang=asin(C.r/dist);
u=u.trunc(sqrt(u.len_sqr()-sqr(C.r)));
a[++cnt]=u.rotate(-ang)+p;
pool[v][++cp[v]]=cnt;
a[++cnt]=u.rotate(ang)+p;
pool[v][++cp[v]]=cnt;
}
inline void getTangents(Cir A,Cir B,int u,int v){
if(A.r<B.r)swap(A,B),swap(u,v);
double d=(A.c-B.c).len();
double base=atan2(B.c.y-A.c.y,B.c.x-A.c.x);
double ang=acos((A.r-B.r)/d); a[++cnt]=A.point(base+ang);
pool[u][++cp[u]]=cnt;
a[++cnt]=B.point(base+ang);
pool[v][++cp[v]]=cnt;
add(cnt-1,cnt,2,u,v); a[++cnt]=A.point(base-ang);
pool[u][++cp[u]]=cnt;
a[++cnt]=B.point(base-ang);
pool[v][++cp[v]]=cnt;
add(cnt-1,cnt,2,u,v); ang=acos((A.r+B.r)/d); a[++cnt]=A.point(base+ang);
pool[u][++cp[u]]=cnt;
a[++cnt]=B.point(PI+base+ang);
pool[v][++cp[v]]=cnt;
add(cnt-1,cnt,2,u,v); a[++cnt]=A.point(base-ang);
pool[u][++cp[u]]=cnt;
a[++cnt]=B.point(PI+base-ang);
pool[v][++cp[v]]=cnt;
add(cnt-1,cnt,2,u,v);
}
inline void solve(int n,int*q,const Cir&C){
if(n<2)return;
int i;
for(i=1;i<=n;i++)w[q[i]]=atan2(a[q[i]].y-C.c.y,a[q[i]].x-C.c.x);
sort(q+1,q+n+1,cmp);
q[n+1]=q[1];
for(i=1;i<=n;i++){
double t=fabs(w[q[i]]-w[q[i+1]]);
t=min(t,PI*2-t)*C.r;
G::add(q[i],q[i+1],t);
G::add(q[i+1],q[i],t);
}
}
int main(){
cnt=2;
a[1].read();
a[2].read();
scanf("%d",&n);
for(i=1;i<=n;i++)b[i].read();
add(1,2,1);
for(i=1;i<=n;i++){
getTangents(a[1],b[i],i);
add(1,cnt-1,1,i);
add(1,cnt,1,i);
getTangents(a[2],b[i],i);
add(cnt-1,2,1,i);
add(cnt,2,1,i);
}
for(i=1;i<=n;i++)for(j=1;j<i;j++)getTangents(b[i],b[j],i,j);
for(i=1;i<=n;i++)solve(cp[i],pool[i],b[i]);
return printf("%.1f",G::solve()),0;
}
BZOJ4739 : 定向越野的更多相关文章
- UOJ277【清华集训2016】定向越野(计算几何,最短路)
UOJ题目传送门 显然最优的路径只会经过若干条两个圆的公切线和若干段圆弧 为了方便,把起点终点看成两个半径为\(0\)的圆也行. 最烦的就是算两个圆的公切线了,一共有四条 对于靠外面的两条,我们把切线 ...
- UOJ #277 BZOJ 4739 定向越野 (计算几何、最短路)
手动博客搬家: 本文发表于20181208 14:39:01, 原地址https://blog.csdn.net/suncongbo/article/details/84891710 哇它居然显示出图 ...
- 清华集训2016Day4
清华集训2016Day4 组合数问题(problem) 用卢卡斯定理可知满足条件即将\(n\)和\(m\)分别用\(k\)进制表示,要求\(n\)的每一位都要大于等于\(m\)的对应位.直接数位\(d ...
- 定向爬虫 - Python模拟新浪微博登录
当我们试图从新浪微博抓取数据时,我们会发现网页上提示未登录,无法查看其他用户的信息. 模拟登录是定向爬虫制作中一个必须克服的问题,只有这样才能爬取到更多的内容. 实现微博登录的方法有很多,一般我们在模 ...
- 【每日一linux命令5】命令的结合与定向
命令中除了一般命令外,还有管道(或称途径)(|)与定向(>或>>). 管道(途径)的用法: "命令一[选项]"|"命令二[选项]",也就是将& ...
- 放养的小爬虫--京东定向爬虫(AJAX获取价格数据)
放养的小爬虫--京东定向爬虫(AJAX获取价格数据) 笔者声明:只用于学习交流,不用于其他途径.源代码已上传github.githu地址:https://github.com/Erma-Wang/Sp ...
- 利用WPS 2012/2013 0day针对中国政府部门的定向攻击
今天早上,我们捕获到一个利用wps 2012/2013 0day针对中国政府部门的钓鱼邮件定向攻击事件. 邮件发件人以2014中国经济形势解析高层报告组委会 标题发出,附件为包含wps2012 0da ...
- Gartner:用自适应安全架构来应对高级定向攻击
发表于2015-06-24 摘要:当前的防护功能难以应对高级的定向攻击,由于企业系统所受到的是持续攻击,并持续缺乏防御力,面向“应急响应”的特别方式已不再是正确的思维模式,Garnter提出了用自 ...
- [python爬虫] Selenium定向爬取海量精美图片及搜索引擎杂谈
我自认为这是自己写过博客中一篇比较优秀的文章,同时也是在深夜凌晨2点满怀着激情和愉悦之心完成的.首先通过这篇文章,你能学到以下几点: 1.可以了解Python简单爬取图片的一些思路和方法 ...
随机推荐
- linux:安装并使用mongo
1.下载mongo: curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.6.tgz 2.解压: tar -zxvf ...
- HTTP协议请求头信息和响应头信息
阅读目录 http的请求部分 常用请头信息 常用响应头信息 http的请求部分 基本结构 请求行 GET /test/hello.html HTTP/1.1 消息头(并不是每一次请求都一样) 空行 ...
- redis centos 6.5 redis版本3.2.8安装过程
redis作为非关系数据库的典型应用,在庞大的数据通信处理有着自己强大的优势,今天也自己来开始学些redis. 以下每一个语句都是我执行的命令. 按照所查资料分析,需要tcl测试工具,这个在cento ...
- Dapper Helper
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using ...
- 一起学Hive——总结各种Join连接的用法
Hive支持常用的SQL join语句,例如内连接.左外连接.右外连接以及HiVe独有的map端连接.其中map端连接是用于优化Hive连接查询的一个重要技巧. 在介绍各种连接之前,先准备好表和数据. ...
- 【Android】android:windowSoftInputMode属性详解
activity主窗口与软键盘的交互模式,可以用来避免输入法面板遮挡问题,Android1.5后的一个新特性. 这个属性能影响两件事情: [一]当有焦点产生时,软键盘是隐藏还是显示 [二]是否减少活动 ...
- nginx+keepalived实现 负载均衡 高可用
Vip: 192.168.220.18 Rip1:192.168.220.2 Rip:192.168.220.3 Rip可以配置在一个服务器上通过ip做虚拟主机 1 rs上配置环境 2 配置应用 Yu ...
- VM VirtualBox – Cannot register the hard disk
第一打开VirtualBox 文件夹,在地址栏输入cmd 第二, 仔细读下面 VBoxManage.exe internalcommands sethduuid "F:\Virtual ...
- 牛客练习赛A 【BFS】
<题目链接> 题目大意: 给出一张图,问你其中 ' # ' 加上那些不能够到达边界的 ' . ' 的点的个数,' # ' 会起阻挡作用. 解题分析: 本题很好做,无非就是将所有能够由边界上 ...
- HDU 4027 Can you answer these queries【线段树】
<题目链接> 题目大意: 给定一段序列,现在对指定区间进行两种操作:一是对指定区间进行修改,对其中的每个数字都开根号(开根号后的数字仍然取整):二是对指定区间进行查询,查询这段区间所有数字 ...