「NOIP 2023 模拟赛 20230717 A」树上最长路 一种奇怪的做法
题意:
给你一棵 个节点的树, 每次询问包含第 条边的树上最长路径长度。
赛时写了 2.5h,确实挺傻逼的。
首先,考虑先用 bfs 求出树的直径,然后用暴力 LCA 标记掉直径所有点。
然后对于所有直径上的节点 ,直接暴力 dfs 到所有非直径点,然后在这些子树上 DP 维护最长链 ,
即
同时维护每个点最近的直径点 和到直径点的距离 。
然后对于每条边,假设离直径点更近的点是 ,答案为
比如说这颗树:
现在要求红边的答案。
那么可以分成绿色、蓝色、紫色三段:
绿色为 ,蓝色为 ,紫色为
那么 在求直径的时候顺便求出来就好了。
总复杂度 ,但是跑得贼慢,吸氧加上 IO 优化才可以卡过 2s。
#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define I inline
#define gc getchar
const int N = 1000010; int h[N], dep[N], nxt[N * 2], to[N * 2], idx, n, a[N], b[N], d[N][3], s, t, fa[N]; bool vis[N], V[N]; int T, p[N], F[N], D[N], mxson[N];
I void add(int a, int b) {to[idx] = b; nxt[idx] = h[a]; h[a] = idx++;}
I void Read(int &x) {
x = 0; char ch = gc(); while(ch < '0' || ch > '9') ch = gc();
while(ch >= '0' && ch <= '9') x = x * 10 + (ch ^ 48), ch = gc();
}
I void Read(int &x, int &y) {Read(x), Read(y);}
I void write(int x) {if(x > 9) write(x / 10); putchar(x % 10 + '0');}
I void writeln(int x) {write(x); putchar('\n');}
I int bfs(int s, int f) {
queue<int> q; for(int i = 1; i <= n; i++) vis[i] = 0; vis[s] = 1; d[s][f] = 0; q.push(s);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = h[u]; i != -1; i = nxt[i]) {
int v = to[i]; if(vis[v]) continue;
vis[v] = 1; d[v][f] = d[u][f] + 1; q.push(v);}
}
int ans = s;
for(int i = 1; i <= n; i++) if(d[i][f] > d[ans][f]) ans = i;
return ans;
}
I void getfa(int u, int f) {p[u] = ++T; dep[u] = dep[f] + 1; fa[u] = f; for(int i = h[u]; i != -1; i = nxt[i]) {int v = to[i]; if(v == f) continue; getfa(v, u);}}
void tag(int a, int b) {
if(dep[a] < dep[b]) swap(a, b); int delta = dep[a] - dep[b];
while(delta--) vis[a] = 1, a = fa[a];
if(a == b) return ;
while(a != b) vis[a] = vis[b] = 1, a = fa[a], b = fa[b];
vis[a] = 1;
}
int S;
I void dfs(int u, int f) {
mxson[u] = 1; F[u] = S;
for(int i = h[u]; i != -1; i = nxt[i]) {
int v = to[i]; if(V[v] || v == f) continue;
V[v] = 1; D[v] = D[u] + 1; dfs(v, u);
mxson[u] = max(mxson[u], mxson[v] + 1);
}
}
int main() {
// freopen("tree.in", "r", stdin); freopen("tree.out", "w", stdout);
Read(n); for(int i = 1; i <= n; i++) h[i] = -1; for(int i = 1; i < n; i++) {Read(a[i], b[i]); add(a[i], b[i]); add(b[i], a[i]);}
s = bfs(1, 0); t = bfs(s, 1); int wyy = bfs(t, 2);
getfa(1, 0); for(int i = 1; i <= n; i++) vis[i] = 0; tag(s, t);
for(int i = 1; i <= n; i++) if(vis[i]) V[i] = 1;
for(int i = 1; i <= n; i++) if(vis[i]) S = i, dfs(i, 0);
for(int i = 1; i < n; i++) {
if(vis[a[i]] && vis[b[i]]) {
writeln(d[t][1]);
}
else {
if(D[a[i]] > D[b[i]]) swap(a[i], b[i]); // cout << "a = " << a[i] << " b = " << b[i] << '\n';
writeln(mxson[b[i]] + D[a[i]] + max(d[F[a[i]]][1], d[F[a[i]]][2]));
}
}
return 0;
}
虽然正解换根 DP 也是线性的,但是这种普及组做法好想,不过我写了 2.5h 实属傻逼
还好最后写出来了,不然彻底成消愁了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现