Official

D - Circumferences Editorial by leaf1415


\(N\) 個の円それぞれに対応した \(N\) 個の頂点をもつグラフを考えます。 \(1 \leq i \lt j \leq N\) を満たすすべての整数の組 \((i, j)\) について \(i\) 番目の円(単に円 \(i\) と呼ぶ)と円 \(j\) が交わるときかつそのときにかぎり、頂点 \(i\) と頂点 \(j\) の間に無向辺を張ります。

本問題を解くには、上記のグラフを構築し、始点 \((s_x, s_y)\) を円周上に含む円の一つ(円 \(S\) とする)と終点 \((t_x, t_y)\) を円周上に含む円の一つ(円 \(T\) とする)を見つけ、グラフ上で頂点 \(S\) から頂点 \(T\) へ到達することができるかを判定すれば良いです。

まず、そのグラフを構築する方法を考えます。 そのためには、円 \(i\) が円 \(j\) と交わるかどうかを判定する必要があります。 下記に示す \(2\) つのパターンにどちらかに該当するとき、かつそのときにかぎり、円 \(i\) と円 \(j\) は交わりません。

  • どちらか一方の円が他方の円の内部にあるケース、すなわち、 \((x_i - x_j)^2 + (y_i - y_j)^2 \lt (r_i - r_j)^2\) を満たすケース

  • どちらの円も他方の円の外部にあるケース、すなわち、 \((x_i - x_j)^2 + (y_i - y_j)^2 \gt (r_i + r_j)^2\) を満たすケース

与えられた \(2\) つの円が上記のパターンに該当するかどうかの判定は \(\mathrm{O}(1)\) 時間で行えるので、\(1 \leq i \lt j \leq N\) を満たすそれぞれの整数の組 \((i, j)\) について円 \(i\) と円 \(j\) が交わるかどうかをそれぞれ判定することで、全体で \(\mathrm{O}(N^2)\) 時間で所望のグラフを構築できます。

また、始点 \((s_x, s_y)\) を円周上に含む円 \(S\) を一つ見つけるには、 点 \((s_x, s_y)\) を円周上に含む必要十分条件

\[ (x_i - s_x)^2 + (y_i - s_y)^2 = r_i^2 \tag{1} \]

を満たす \(i\) を見つければ良いです。 これはそれぞれの \(i = 1, 2, \ldots, N\) について (1) が成り立つかどうかを調べることで全体で \(\mathrm{O}(N)\) 時間で可能です。 終点 \((t_x, t_y)\) を円周上に含む円 \(T\) を一つ見つけることも同様です。

構築したグラフ上で頂点 \(S\) から頂点 \(T\) へ到達することができるかどうかの判定は、グラフ上の探索アルゴリズム(深さ優先探索など)によって、\(\mathrm{O}(N^2)\) 時間で行うことができます。

以上で、本問題を \(\mathrm{O}(N^2)\) 時間で解くことができます。

C++言語による本問題の正解例を記載します。

#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;

int n;
ll sx, sy, tx, ty;
ll x[3005], y[3005], r[3005];
vector<int> G[3005];

int S, T;
bool used[3005];

bool dfs(int v)
{
  used[v] = true;
  if(v == T) return true;
  for(auto u : G[v]){
    if(used[u]) continue;
    if(dfs(u)) return true;
  }
  return false;
}

int main(void)
{
  cin >> n;
  cin >> sx >> sy >> tx >> ty;
  for(int i = 1; i <= n; i++) cin >> x[i] >> y[i] >> r[i];

  for(int i = 1; i <= n; i++){
    for(int j = i+1; j <= n; j++){
      ll d = (x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]);
      if(d > (r[i]+r[j])*(r[i]+r[j]) || d < (r[i]-r[j])*(r[i]-r[j])) continue;
      G[i].push_back(j), G[j].push_back(i);
    }
  }

  for(int i = 1; i <= n; i++){
    if((x[i]-sx)*(x[i]-sx) + (y[i]-sy)*(y[i]-sy) == r[i]*r[i]) S = i;
    if((x[i]-tx)*(x[i]-tx) + (y[i]-ty)*(y[i]-ty) == r[i]*r[i]) T = i;
  }

  if(dfs(S)) cout << "Yes" << endl;
  else cout << "No" << endl;

  return 0;
}

posted:
last update: