2022-07-02 23:10阅读: 77评论: 0推荐: 0

Codeforces 1695D. Tree Queries

传送门

Easy Version

Difficulty:2200

题目大意

一棵 n(1n2000) 个节点的树,有一个未知节点 x ,每次询问一个节点 v ,得到 dis(v,x) ,求最少询问几个节点,在所有情况下,都能够唯一确定 x

思路

考虑树形 dp 。设 fv 为在 v 为根的子树中,当 v 的向上子树已经有节点被询问的情况下,确定任意一点所需的最少查询数,记 cntvv 的儿子中 f=0 的数量,我们当前仅需要能够确定节点在哪个子树中即可,因为我们进入任意的子树后都可以去确定节点。当 cnt>2 时,显然无法确定,否则一定可以确定,因为如果节点 x 在没有询问的子树当中,对任意其他子树中被询问的点为 y ,记 v 向上子树中被询问的一个点为 z ,于是一定有 dis(z,x)+dis(z,y)2dis(z,v) ,否则存在不满足(= 变为 >)的点。那么我们如果 cntv 过大,我们对没有询问的子树补上一个询问节点即可,于是可以得出转移方程:

fv=uson[v]fu+max(cntv1,0)

此外叶节点的 f=0,最后答案为 froot+1 通过强制选取跟来保证各个节点满足状态定义,由于根对答案有影响,要枚举根取最小值,复杂度 O(n2)

代码

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mk make_pair
//#define int LL
//#define lc P*2
//#define rc P*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000009;
const LL mod = 998244353;
const int maxn = 200010;

int T, N, f[maxn];
vector<int>G[maxn];

void add_edge(int from, int to)
{
    G[from].push_back(to);
    G[to].push_back(from);
}

void dfs(int v, int p)
{
    int cnt = 0;
    for (auto& to : G[v])
    {
        if (to == p)
            continue;
        dfs(to, v);
        if (!f[to])
            cnt++;
        f[v] += f[to];
    }
    f[v] += max(cnt - 1, 0);
}

void solve()
{
    if (N == 1)
    {
        cout << 0 << endl;
        return;
    }
    int ans = N;
    for (int i = 1; i <= N; i++)
    {
        dfs(i, 0), ans = min(ans, f[i] + 1);
        for (int j = 1; j <= N; j++)
            f[j] = 0;
    }
    cout << ans << endl;
}

int main()
{
    IOS;
    cin >> T;
    while (T--)
    {
        cin >> N;
        for (int i = 1; i <= N; i++)
            G[i].clear();
        int x, y;
        for (int i = 1; i < N; i++)
            cin >> x >> y, add_edge(x, y);
        solve();
    }

    return 0;
}

Hard Version

Difficulty:2300
Easy Version 唯一的区别是 1n2105

思路

在简单版本的做法中,我们为了保证状态的定义,强制选取了根,但我们也可以不选取根,如果一个节点的度 >2 ,那么以该节点为根时,其至少两个儿子的子树内有节点被选,于是可以保证整棵树的任意一个节点的向上子树中都有节点被选,于是该根可以不用被选取,于是我们找一个这样的跟,进行一遍简单版本的 dp 即为答案,注意此时最后的答案不需要 +1 ,此外如果没有这样的根,说明为一条链,答案为 1

代码

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mk make_pair
//#define int LL
//#define lc P*2
//#define rc P*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000009;
const LL mod = 998244353;
const int maxn = 200010;

int T, N, f[maxn], deg[maxn];
vector<int>G[maxn];

void add_edge(int from, int to)
{
    G[from].push_back(to);
    G[to].push_back(from);
}

void dfs(int v, int p)
{
    int cnt = 0;
    for (auto& to : G[v])
    {
        if (to == p)
            continue;
        dfs(to, v);
        if (!f[to])
            cnt++;
        f[v] += f[to];
    }
    f[v] += max(cnt - 1, 0);
}

void solve()
{
    if (N == 1)
    {
        cout << 0 << endl;
        return;
    }
    int root = 0;
    for (int i = 1; i <= N; i++)
    {
        if (deg[i] > 2)
        {
            root = i;
            break;
        }
    }
    if (!root)
    {
        cout << 1 << endl;
        return;
    }
    dfs(root, 0);
    cout << f[root] << endl;
}

int main()
{
    IOS;
    cin >> T;
    while (T--)
    {
        cin >> N;
        for (int i = 1; i <= N; i++)
            G[i].clear(), deg[i] = 0, f[i] = 0;
        int x, y;
        for (int i = 1; i < N; i++)
            cin >> x >> y, add_edge(x, y), deg[x]++, deg[y]++;
        solve();
    }

    return 0;
}

本文作者:Prgl

本文链接:https://www.cnblogs.com/Prgl/p/16438616.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Prgl  阅读(77)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开