其他分享
首页 > 其他分享> > cf1695 D2. Tree Queries (Hard Version)

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