持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
ACM2022河南省赛——J. Mex Tree
Problem J. Mex Tree
小水獭幼儿园的同学们都十分擅长图论内容,作为优等生的 JJLeo 更是如此。
某天,隔壁金州幼儿园的 Bellalabella 向 JJLeo 请教了这样一个问题。
给定一棵 n 个点的树,编号为 1, 2, . . . , n,第 i 个点的权值为 vi,v1, v2, . . . , vn 是 0, 1, . . . , n − 1 的一个排列。对于 k = 0, 1, . . . , n,Bellalabella 想找到这棵树的一个非空连通子图,其点集 S 满足 mex({vi| i ∈ S})恰好为 k。如果存在这样的非空连通子图,Bellalabella 想知道点数最多的非空连通子图有多少个点。其中 mex(S) 表示不属于 S 的最小非负整数,例如:
- mex({0, 1, 2}) = 3,因为 0, 1, 2 均在 {0, 1, 2} 中,而 3 是不在集合中的最小非负整数。
- mex({1, 2}) = 0,因为 0 不在集合中。
JJLeo 虽然精通树的知识,但是对于 mex 运算却毫无办法,你能帮他解决这个问题吗?
输入
第一行,一个整数 n(1 ≤ n ≤ 106),表示树的结点个数。 第二行,n 个整数 v1, v2, . . . , vn,表示每个点的权值。保证 v1, v2, . . . , vn 是 0, 1, . . . , n − 1 的一个排 列。 若 n > 1,第三行,n − 1 个整数 f2, f3, . . . , fn(fi < i),表示树上存在边 (fi, i)。
输出
一行以空格分隔的 n + 1 个整数,当 k = i(i = 0, 1, . . . , n)时,若不存在满足条件的非空连通子图,第 i + 1 个数为 0,否则第 i + 1 个数为满足条件且点数最多的非空连通子图的点数。
样例
输入
3
0 1 2
1
输出
1 2 2 3
问题解析
首先对于这类不给定根的树,我们可以假设一个点作为他的根来简化问题,这里设0作为它的根。
然后我们知道,如果想要一个连通块的mex为k,那么这个连通块中要包含所有小于k的点。
那么,对于一个mex为k的连通块,我们分类讨论:
- 当k=0时,如果点0连着的只有一个孩子,答案就是这个孩子的大小;如果连着的多个孩子,那么显然我们只能选一个孩子,如果要选多个,为了保证他们是连通块我们则要选上0,这样mex就不是0了,所以我们只能选择其中一个孩子,因为我们想要连通块点最多,所以当k等于0时,答案就是0的子树中,点最多的那一个
- 当k=(1,2,……,n-1)时,由于0是根节点了,所以如果有答案,则可能的答案是:除了点k这棵树以外的所有点,因为如果我们选择点k的子树,他的子树一定没有0,mex会是0,不满足mex是k。所以可能的答案只能是除去点k这一整颗树之外的所有点。但如果它的子树里有一个点的值u小于k,说明外界的那些点没有u,那么外界连通块的mex是u,也不满足k,此时答案是0。
- 当k=n时,我们直接选择整棵树即可。
所以,我们可以一遍dfs来求每个子树所包含的最小值和子树的大小size。如果k树的最小值mn小于k,则答案为0;否则答案为n-size[k]
时间复杂度:O(n); (dfs只用每个点跑一遍)
空间复杂度:O(n);
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50;
vector<int>tree[N];
int a[N], f[N], mysize[N], n;
int dfs(int x, int y)
{
int mn = 1e9;
mysize[x] = 1;
for (auto i : tree[x])
{
if (i == y)continue;
mn = min(mn, dfs(i, x));
mysize[x] += mysize[i];
}
if (mn > x)f[x] = n - mysize[x];
else f[x] = 0;
mn = min(mn, x);
return mn;
}
void solve()
{
int x, y;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 2; i <= n; i++)
{
cin >> x;
tree[a[i]].push_back(a[x]);
tree[a[x]].push_back(a[i]);
}
dfs(0, -1);
f[n] = n;
int mn = 0;
for (auto i : tree[0])mn = max(mn, mysize[i]);
f[0] = mn;
for (int i = 0; i <= n; i++)
{
cout << f[i] << " ";
}
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
\