[二分 bfs] UOJ#371. 【UR #17】滑稽树下你和我

二分答案
用点对 (x,y) ( x , y ) 表示一个人在 x x ,另一个在 y 的状态,当 x x y 的距离小于等于二分的答案时,这个状态合法。
两个状态 (x1,y1) ( x 1 , y 1 ) (x2,y2) ( x 2 , y 2 ) 直接相连,当且仅当 x1 x 1 x2 x 2 之间有边或者 y1 y 1 y2 y 2 有边
那么只要用bfs判断一下 (stx,sty) ( s t x , s t y ) 是否与一个由两个叶子构成的状态相连。

但是两个人是可以任意走动的,也就是在边上也要算

可以把边拆成一个点,一个点和一条边的距离是点到线段的最短距离,边与边的距离是其中一条边的两个端点到另一条边的距离较大值的最小值

然后就跟之前一样判断就好了

考场上点到线段的距离写错了炸成5分…

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#define debug(x) //cerr<<#x<<' '<<x<<endl;
#define fi first
#define se second

using namespace std;

typedef pair<int,int> pii;

const int N=2010;

struct Point{
  double x,y;
  Point(){}
  Point(double _x,double _y):x(_x),y(_y){}
  friend Point operator -(Point a,Point b){
    return Point(a.x-b.x,a.y-b.y);
  }
  double len2(){
    return x*x+y*y;
  }
  double len(){
    return sqrt(len2());
  }
};

struct Line{
  Point a,b;
  Line(){}
  Line(Point _a,Point _b):a(_a),b(_b){}
  double len2(){
    return (a-b).len2();
  }
  double len(){
    return sqrt(len2());
  }
}a[N<<1];

int n,x,y,t,cnt,G[N],vis[N][N];
struct edge{
  int t,nx;
}E[N<<1];

inline double dot(Point a,Point b){
  return a.x*b.x+a.y*b.y;
}

inline double cross(Point a,Point b){
  return a.x*b.y-a.y*b.x;
}
int du[N];

inline void addedge(int x,int y){
  E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt; du[x]++;
  E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt; du[y]++;
}

inline double dis(Point x,Line y){
  double dt=dot(y.a-y.b,x-y.b);
  if(dt>0 && dt<y.len2()) return fabs(cross(y.a-y.b,x-y.b)/y.len());
  debug((x-y.a).len()); debug((x-y.b).len());
  return min((x-y.a).len(),(x-y.b).len());
}

inline double dist(Line x,Line y){
  return min(max(dis(x.a,y),dis(x.b,y)),max(dis(y.a,x),dis(y.b,x)));
}

double D[N][N];
queue<pii> Q;
vector<int> sb;

inline bool check(double cc){
  for(int i=1;i<=t;i++)
    for(int j=1;j<=t;j++)
      vis[i][j]=0;
  while(!Q.empty()) Q.pop();
  Q.push(pii(x,y)); vis[x][y]=vis[y][x]=1;
  while(!Q.empty()){
    int x=Q.front().fi,y=Q.front().se; Q.pop();
    for(int i=G[x];i;i=E[i].nx)
      for(int j=G[y];j;j=E[j].nx)
    if(!vis[E[i].t][E[j].t] && cc-D[E[i].t][E[j].t]>-1e-6){
      vis[E[i].t][E[j].t]=vis[E[j].t][E[i].t]=1;
      if(du[E[i].t]==1 && du[E[j].t]==1) return true;
      Q.push(pii(E[i].t,E[j].t));
    }
    for(int i=G[x];i;i=E[i].nx)
      if(!vis[E[i].t][y] && cc-D[E[i].t][y]>-1e-6){
    vis[E[i].t][y]=vis[y][E[i].t]=1;
    if(du[E[i].t]==1 && du[y]==1) return true;
    Q.push(pii(E[i].t,y));
      }
    for(int i=G[y];i;i=E[i].nx)
      if(!vis[E[i].t][x] && cc-D[E[i].t][x]>-1e-6){
    vis[E[i].t][x]=vis[x][E[i].t]=1;
    if(du[E[i].t]==1 && du[x]==1) return true;
    Q.push(pii(E[i].t,x));
      }
  }
  return false;
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  scanf("%d%d%d",&n,&x,&y);
  for(int i=1;i<=n;i++){
    int cx,cy; scanf("%d%d",&cx,&cy);
    sb.push_back(cx); sb.push_back(cy);
    a[i]=Line(Point(cx,cy),Point(cx,cy));
  }
  t=n;
  for(int i=1;i<n;i++){
    ++t; int cx,cy; scanf("%d%d",&cx,&cy);
    a[t]=Line(a[cx].a,a[cy].a);
    addedge(cx,t); addedge(cy,t);
  }
  double L=dist(a[x],a[y]),R=0,mid,ret;
  for(int i=1;i<=t;i++)
    for(int j=1;j<=t;j++)
      R=max(R,D[i][j]=dist(a[i],a[j]));
  double cur=dist(a[n+2],a[n+6]);
  debug(cur);
  if(du[x]==1 && du[y]==1) return printf("%.9lf\n",D[x][y]),0;
  while(R-L>1e-6)
    check(mid=(L+R)/2)?R=mid:L=mid;
  printf("%.9lf\n",(L+R)/2);
  return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值