每日一题 8月18日MMSet2 求树上集合点的最大距离最小
题目
MMSet2(https://ac.nowcoder.com/acm/problem/14250)
题目描述
给定一棵n个节点的树,点编号为1…n。
Q次询问,每次询问给定一个点集S,
其中dist(u,v)表示树上路径(u,v)的边数。
输入描述:
第一行一个整数n,接下来n−1行每行两个整数表示树上的一条边。
接下来一行一个整数Q,接着Q行,每行第一个数是|S|,剩下|S|个互不相同的数代表这个集合。
输出描述:
输出Q行,每行一个整数表示答案。
输入
3
1 2
1 3
1
2 2 3
输出
1
备注:
n≤3×105,|S|≥1,∑|S|≤106
思路
题目求树上任意选择一点到|S|集合的点的最大距离最小。这个点应该是|S|中距离最远点的点对的中点。
假设为x,y。ans=(dis(x, y)+1)/2
现在怎么求|S|的直径。
先以1为根维护出每个点的深度,那么S集合里面深度最深的一个点就是最长距离一个端点。
然后在|S|枚举另外一个点就可以了。这个用树上LCA就可以了。
#pragma GCC optimize(3, "Ofast", "inline")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization ("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define rint register int
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
char buf[1<<20],*p1=buf,*p2=buf;
inline int read() {
int f=0,fu=1;
char c=getchar();
while(c<'0'||c>'9') {
if(c=='-')
fu=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
f=(f<<3)+(f<<1)+c-48;
c=getchar();
}
return f*fu;
}
inline void print(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x >= 10)
print(x / 10);
putchar(x % 10 + '0');
}
const int N = 300015;
struct Edge {
int to, nxt, val;
} edge[N << 1];
struct RMQ_lca {
LL w[N];
int head[N], etot;
int lg[N << 1], a[N << 1], dfn[N], dep[N], tot;
int f[N << 1][20];
void init(int n) {
etot=tot=0;
memset(head, 0, sizeof(head[0])*(n+5));
memset(w, 0, sizeof(w[0])*(n+5));
}
void add(int u, int v, int w) {
edge[++etot] = {v, head[u], w};
head[u] = etot;
}
void dfs(int u, int fa) {
f[++tot][0] = u, dfn[u] = tot, dep[u] = dep[fa] + 1;
for(rint i = head[u]; i; i = edge[i].nxt) {
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) {
//cout<<u<<" "<<v<<" "<<LCA(u, v)<<endl;
return w[u] + w[v] - 2ll * w[LCA(u, v)];
}
} lca;
vector<int> v;
int main() {
int n=read();
lca.init(n);
for(int i=1; i<n; i++) {
int x, y;
x=read(), y=read();
lca.add(x, y, 1);
lca.add(y, x, 1);
}
lca.dfs(1, 0);
lca.pre();
int q=read();
while(q--) {
int m=read();
v.clear();
int mx=0, ans=0, x;
while(m--) {
x=read();
v.push_back(x);
if(lca.dep[mx]<lca.dep[x]) {
mx=x;
}
}
for(auto x: v) {
//cout<<x<<" "<<mx<<" "<<((lca.dist(x, mx)+1)/2)<<endl;
ans=max(1ll*ans, (lca.dist(x, mx)+1)/2);
}
print(ans); 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)