CF33D Knights(图论建模(+虚根技巧) + lca,好题)

link

这道题一眼看上去就很不一样,但从题意的 m + 1 个区域和 m 个栅栏很容易想到,这应该是个树上的问题。

题目里的圆有很好的性质:圆之间不相交,只存在包含与相离的位置关系

摸一下样例,发现就是一个起点,必须越过多少个圆能到达终点

如果把这些圆抽象地理解一下,就是(u, v)路径上的点,同心圆一样层层包含的圆就像树上一个一个点往上跳,组成一颗子树,而包含不同圆的大圆就像这些子树的公共祖先,有一种父子关系在里边。

这个性质很完美,太完美了,这样不就建个图跑 lca 求路径长度就好了吗?哈哈

思路就这么简单,但图论建模一是难在建模思路,二就是怎样实现了。(我代码也码了差不多 40 分钟 ...)


在这里我列出一下我建图时遇到的一些要点:

  • 要逐层建边,不能跃层,要不然就会成环,这里我用 O(m2) 对每个圆打擂台,找 包含它的最小的圆

  • 对于每个点(题目中的控制点),要找到 包含它的最小的圆,也就是对应着它在树上的点 ,也可以 O(m) 打擂台(这里都要用到一些简单的几何知识求距离,很简单)

  • 还有一个 trick,就是如果一个控制点没有圆包含它,只是这样做的话,它就无法进入树中,这里可以 建立虚根,就像在坐标图中做一个极大的圆把所有的圆都包含进去,这样就保证了所有的点都可以有树中对应的位置,同时,虚根对所有点都相当于增加了一个深度,对结果的 路径距离(相对值) 没有影响。

最后答案就是路径长度 dep[u]+dep[v]2dep[ lca(u,v) ]

O(km) .

#include <bits/stdc++.h>
#define re register int
using namespace std;
const int N = 3e5 + 10, logN = 50;
const double inf = 2000000010.00;
struct Edge
{
int to, next;
}e[N << 1];
int top, h[N];
int dep[N], f[N][logN];
struct Node
{
double x, y;
}a[N];
struct Round
{
double x, y, r;
}pai[N];
int n, m, k;
inline double cale1(Round i, Round j)
{
return sqrt((i.x - j.x) * (i.x - j.x) + (i.y - j.y) * (i.y - j.y));
}
inline double cale2(Node i, Round j)
{
return sqrt((i.x - j.x) * (i.x - j.x) + (i.y - j.y) * (i.y - j.y));
}
inline void add(int x, int y)
{
e[++ top] = (Edge){y, h[x]};
h[x] = top;
}
inline void build()
{
// 逐层建边,不能跃层
double delta = inf, tag;
for (re i = 1; i <= m; i ++)
{
delta = inf, tag = 0;
for (re j = 1; j <= m; j ++)
{
if (j == i) continue;
double w = cale1(pai[i], pai[j]);
if (w > pai[i].r + pai[j].r || pai[j].r < pai[i].r) continue; // 相离 或 内包含圆
if (w == 0)
{
if (pai[j].r - pai[i].r < delta)
{
delta = pai[j].r - pai[i].r;
tag = j;
}
}
else
{
if (pai[j].r - 2 * pai[i].r < delta)
{
delta = pai[j].r - 2 * pai[i].r;
tag = j;
}
}
}
if (delta == inf) add(i, 0), add(0, i);
else add(i, tag), add(tag, i);
}
}
int dfs(int u, int fa)
{
if (fa != -1) dep[u] = dep[fa] + 1;
f[u][0] = fa;
for (re i = 1; i <= log2(m); i ++)
f[u][i] = f[f[u][i - 1]][i - 1];
for (re i = h[u]; i; i = e[i].next)
{
int v = e[i].to;
if (v == fa) continue;
dfs(v, u);
}
}
inline int lca(int u, int v)
{
if (dep[u] < dep[v]) swap(u, v);
for (re i = log2(m); i >= 0; i --)
if (dep[f[u][i]] >= dep[v]) u = f[u][i];
if (u == v) return u;
for (re i = log2(m); i >= 0; i --)
if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
return f[u][0];
}
inline int get(int x)
{
double delta = inf;
int tag = 0;
for (re i = 1; i <= m; i ++)
{
double d = cale2(a[x], pai[i]);
if (d > pai[i].r) continue;
if (pai[i].r - d < delta)
{
delta = pai[i].r - d;
tag = i;
}
}
return tag;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m >> k;
for (re i = 1; i <= n; i ++) cin >> a[i].x >> a[i].y;
for (re i = 1; i <= m; i ++) cin >> pai[i].r >> pai[i].x >> pai[i].y;
build();
dep[0] = 1;
dfs(0, -1);
while (k --)
{
int a, b; cin >> a >> b;
int u = get(a), v = get(b);
cout << dep[u] + dep[v] - 2 * dep[(lca(u, v))] << '\n';
}
return 0;
}
posted @   Zhang_Wenjie  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示