CF33D Knights(图论建模(+虚根技巧) + lca,好题)
这道题一眼看上去就很不一样,但从题意的 m + 1 个区域和 m 个栅栏很容易想到,这应该是个树上的问题。
题目里的圆有很好的性质:圆之间不相交,只存在包含与相离的位置关系
摸一下样例,发现就是一个起点,必须越过多少个圆能到达终点
如果把这些圆抽象地理解一下,就是(u, v)路径上的点,同心圆一样层层包含的圆就像树上一个一个点往上跳,组成一颗子树,而包含不同圆的大圆就像这些子树的公共祖先,有一种父子关系在里边。
这个性质很完美,太完美了,这样不就建个图跑 lca 求路径长度就好了吗?哈哈
思路就这么简单,但图论建模一是难在建模思路,二就是怎样实现了。(我代码也码了差不多 40 分钟 ...)
在这里我列出一下我建图时遇到的一些要点:
-
要逐层建边,不能跃层,要不然就会成环,这里我用
对每个圆打擂台,找 包含它的最小的圆 -
对于每个点(题目中的控制点),要找到 包含它的最小的圆,也就是对应着它在树上的点 ,也可以
打擂台(这里都要用到一些简单的几何知识求距离,很简单) -
还有一个 trick,就是如果一个控制点没有圆包含它,只是这样做的话,它就无法进入树中,这里可以 建立虚根,就像在坐标图中做一个极大的圆把所有的圆都包含进去,这样就保证了所有的点都可以有树中对应的位置,同时,虚根对所有点都相当于增加了一个深度,对结果的 路径距离(相对值) 没有影响。
最后答案就是路径长度
#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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具