cf1695 D2. Tree Queries (Hard Version)
作者:互联网
题意:
给定一棵边权为1的树,树中有一个特殊节点但不告诉你是哪个。你可以询问一个节点集 \(S\),然后知道特殊点到 \(S\) 中每个点的距离。输出能确定特殊点的 \(|S|\) 的最小值
\(n\le 2e5\)
思路:
题目转化为:对任意点 \(u\),怎样区分特殊点与 \(u\)?
再转化为:要能区分任意两点。即对任意两点 \(u\neq v\),存在点 \(x\in S\),\(dis_{x,u}\neq dis_{x,v}\)
若 \(u\) 到 \(v\) 的距离为奇数,那只要整棵树中至少有一个询问点就能区分 \(u,v\) 了
若 \(u\) 到 \(v\) 的距离为偶数,设 \(u\) 到 \(v\) 的链形如 \(u-\dots -x-mid-y-\dots v\),其中 \(mid\) 是中点(即 \(dis_{mid,u}=dis_{mid,v}\))
为方便叙述,暂时把 \(mid\) 看成根——那么 \(x\)子树或 \(y\)子树中至少要有一个询问点
最终结论:对任一节点 \(u\),设 \(u\) 的邻点们为 \(v_i\),则最多只有一棵 \(v_i\)子树可以没有询问点
怎么实现呢?设当 \(u\) 不为根时,\(ans[u]\) 表示 \(u\)子树之外已经有询问点的情况下,\(u\)子树中至少要选几个点。dfs 时统计 \(u\) 有几个 “子树中没有询问点的儿子”,最多允许一个儿子的子树中没有询问点
怎么保证 “\(u\)子树之外已经有询问点”?
若整棵树的根节点的度大于等于3,这肯定满足;
而若根节点的度为2,有可能 \(u\)子树之外没有询问点。所以我们可以选一个度数大于等于3的点开始 dfs 以避免这种情况;
如果找不到度数大于等于3的点说明整棵树是一条链,答案是1;另外还要特判整棵树只有一个点的情况
int n, d[N]; vector<int> G[N];
int dfs(int u, int fa) {
int ans = 0, cnt = 0; //子树中没有点的儿子的数量
for(int v : G[u]) if(v != fa) {
int t = dfs(v, u);
if(t) ans += t; else cnt++;
}
return ans + max(0, cnt - 1);
}
int sol() {
int n; cin >> n;
for(int i = 1; i <= n; i++) G[i].clear(), d[i] = 0;
for(int i = 1; i < n; i++) {
int x, y; cin >> x >> y;
G[x].pb(y), G[y].pb(x);
d[x]++, d[y]++;
}
if(n == 1) return 0; //只有一个点
for(int i = 1; i <= n; i++)
if(d[i] >= 3) return dfs(i, 0);
return 1; //链
}
标签:子树,int,询问,Hard,Tree,mid,dfs,子树中,cf1695 来源: https://www.cnblogs.com/wushansinger/p/16395347.html