Codeforces 1695D. Tree Queries
Easy Version
题目大意
一棵 个节点的树,有一个未知节点 ,每次询问一个节点 ,得到 ,求最少询问几个节点,在所有情况下,都能够唯一确定 。
思路
考虑树形 。设 为在 为根的子树中,当 的向上子树已经有节点被询问的情况下,确定任意一点所需的最少查询数,记 为 的儿子中 的数量,我们当前仅需要能够确定节点在哪个子树中即可,因为我们进入任意的子树后都可以去确定节点。当 时,显然无法确定,否则一定可以确定,因为如果节点 在没有询问的子树当中,对任意其他子树中被询问的点为 ,记 向上子树中被询问的一个点为 ,于是一定有 ,否则存在不满足( 变为 )的点。那么我们如果 过大,我们对没有询问的子树补上一个询问节点即可,于是可以得出转移方程:
此外叶节点的 ,最后答案为 通过强制选取跟来保证各个节点满足状态定义,由于根对答案有影响,要枚举根取最小值,复杂度 。
代码
#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
与 唯一的区别是 。
思路
在简单版本的做法中,我们为了保证状态的定义,强制选取了根,但我们也可以不选取根,如果一个节点的度 ,那么以该节点为根时,其至少两个儿子的子树内有节点被选,于是可以保证整棵树的任意一个节点的向上子树中都有节点被选,于是该根可以不用被选取,于是我们找一个这样的跟,进行一遍简单版本的 即为答案,注意此时最后的答案不需要 ,此外如果没有这样的根,说明为一条链,答案为 。
代码
#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 中国大陆许可协议进行许可。
分类:
,
标签:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步