牛客练习赛67 F 牛妹的苹果树 倍增DP+树的直径+LCA
题目
牛客练习赛67 F牛妹的苹果树(https://ac.nowcoder.com/acm/contest/6885/F)
题目描述
牛妹种了一棵苹果树。
这棵苹果树有n个节点,n-1条边,每一条边都有一个权值wi
我们定义:这棵树上的两点之间距离dist(u,v)为它们简单路径上所有边的权值和。
现在,牛妹想给你q次询问,每次询问一个区间[l,r],求max(dist(u,v)), l≤u≤v≤r。
输入描述:
第一行,读入n和q。
接下来n-1行,每行读入u,v和w,表示一条边。
接下来q行,每行读入l和r,表示一组询问。
输出描述:
对于每一组询问,输出对应的最大距离值。
示例1
输入
3 3
1 2 20
2 3 40
1 1
1 3
1 2
输出
0
60
20
说明
第一组询问,最长距离是节点1到节点1,距离为0;
第二组询问,最长距离是节点1到节点3,距离为20+40=60;
第三组询问,最长距离是节点1到节点2,距离为20.
备注
数据保证1<n,q<300000,1<=wi<=10^9,1<=l<=r≤n,q≤300000
思路
查询的就是这些点的直径
询问是区间连续的,我们考虑倍增:
因为一个性质:
查询的时候和st表一样就可以了。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define LL long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define each(i) for (rint i = head[u]; i; i = edge[i].nxt)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
const int N = 500015;
int n, q;
struct Edge {
int to, nxt, val;
} edge[N << 1];
int head[N], etot;
void add(int u, int v, int w) {
edge[++etot] = {v, head[u], w};
head[u] = etot;
}
ll w[N];
int lg[N << 1], a[N << 1], dfn[N], dep[N], tot;
int f[N << 1][20];
void dfs(int u, int fa) {
f[++tot][0] = u, dfn[u] = tot, dep[u] = dep[fa] + 1;
each(i) {
int v = edge[i].to;
if (v == fa)
continue;
w[v] = w[u] + edge[i].val;
dfs(v, u);
f[++tot][0] = u;
}
}
void pre() {
lg[1] = 0;
for (rint i = 2; i <= tot; i++)
lg[i] = lg[i >> 1] + 1;
for (rint j = 1; j <= 19; j++) {
for (rint i = 1; i + (1 << j) - 1 <= tot; i++) {
if (dep[f[i][j - 1]] < dep[f[i + (1 << j - 1)][j - 1]]) {
f[i][j] = f[i][j - 1];
} else {
f[i][j] = f[i + (1 << j - 1)][j - 1];
}
}
}
}
int LCA(int u, int v) {
u = dfn[u], v = dfn[v];
if (u > v)
swap(u, v);
int len = lg[v - u + 1];
if (dep[f[u][len]] < dep[f[v - (1 << len) + 1][len]]) {
return f[u][len];
} else {
return f[v - (1 << len) + 1][len];
}
}
ll dist(int u, int v) {
return w[u] + w[v] - 2ll * w[LCA(u, v)];
}
inline ll read() {
ll x = 0, neg = 1;
char op = getchar();
while (!isdigit(op)) {
if (op == '-')
neg = -1;
op = getchar();
}
while (isdigit(op)) {
x = 10 * x + op - '0';
op = getchar();
}
return neg * x;
}
#define pii pair<LL, LL>
#define f first
#define s second
pii g[500010][20];
pii ME(pii x, pii y) {//合并树求直径
LL dis[6]= {dist(x.f, x.s), dist(x.f, y.f), dist(x.f, y.s), dist(y.f, y.s), dist(x.s, y.f), dist(x.s, y.s)};
LL mx=0;
for(LL i=0; i<6; i++) {
if(dis[i]>dis[mx]) {
mx=i;
}
}
switch (mx) {
case 0:
return make_pair(x.f, x.s);
case 1:
return make_pair(x.f, y.f);
case 2:
return make_pair(x.f, y.s);
case 3:
return make_pair(y.f, y.s);
case 4:
return make_pair(x.s, y.f);
case 5:
return make_pair(x.s, y.s);
}
}
void slove(LL n) {//倍增
for(LL i=1; i<=n; i++) {
g[i][0]= make_pair(i, i);
}
for(LL j=1; j<20; j++) {
for(LL i=1; i<=n; i++) {
if(i+(1<<j)-1<=n) {
g[i][j]=ME(g[i][j-1], g[i+(1<<j-1)][j-1]);
}
}
}
}
pii calc(LL l, LL r) {
LL len=lg[r-l+1];
return ME(g[l][len], g[r-(1<<len)+1][len]);
}
inline void print(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x >= 10)
print(x / 10);
putchar(x % 10 + '0');
}
int main() {
lg[1]=0;
for(int i=2; i<=300005; i++) {
lg[i]=lg[i>>1]+1;
}
LL n, q;
n=read(), q=read();
for(LL i = 1; i <= n-1; ++i) {
LL x, y, w;
x=read(), y=read(), w=read();
add(x, y, w);
add(y, x, w);
}
dfs(1, 0);
pre();
slove(n);
for(LL i = 1; i <= q; ++i) {
LL x, y;
x=read(), y=read();
pii ans=calc(x, y);
print(dist(ans.f, ans.s));
putchar('\n');
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)