力扣2003 三种解法 启发式合并线段树维护mex运算思维
Posted hesorchen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣2003 三种解法 启发式合并线段树维护mex运算思维相关的知识,希望对你有一定的参考价值。
题目链接
给出一颗有根树,每个节点有一个权值 w i w_i wi,求出所有子树的mex值,一个子树的mex值定义为在该子树中最小的未出现的正整数。
题解
Solution I
启发式合并
考虑暴力维护每颗子树包含的数据,当前子树包含的正整数就是所有儿子包含的正整数加上根,并且根据mex运算性质,父亲结点的mex值肯定大于等于儿子结点。
但是暴力合并的时空复杂度都是 O ( n 2 ) O(n^2) O(n2)。可以在合并的时候每次都将小的集合往大的集合合并,这样时间复杂度最坏为 O ( n l o g n ) O(nlogn) O(nlogn)。(当树为满二叉树的时候)
代码:
class Solution
{
public:
vector<int> mp[400005];
set<int> st[100005];
vector<int> mex;
void dfs(int u, int fa)
{
for (auto v : mp[u])
{
if (v == fa)
continue;
dfs(v, u);
if (st[u].size() < st[v].size())
swap(st[u], st[v]);
for (auto it : st[v])
st[u].insert(it);
mex[u] = max(mex[u], mex[v]);
}
while (st[u].count(mex[u]))
++mex[u];
}
vector<int> smallestMissingValueSubtree(vector<int> &parents, vector<int> &nums)
{
int n = parents.size();
mex = vector<int>(n, 1);
for (int i = 0; i < n; i++)
st[i].insert(nums[i]);
for (int i = 0; i < n; i++)
{
int fa = parents[i];
if (fa == -1)
continue;
mp[i].emplace_back(fa);
mp[fa].emplace_back(i);
}
dfs(0, -1);
return mex;
}
};
Solution II
可以发现,权值为1到根结点这一条链上的答案不为一,其他点的答案都为1。因此可以考虑拿出这一条链暴力维护。有上面mex的性质,时间复杂度为 O ( n ) O(n) O(n)。
代码:
class Solution
{
public:
vector<int> mp[100005];
vector<int> mex, vis, vis2, NUMS;
int goal, MEX, N;
int dfs(int u, int fa)
{
bool f = 0;
for (auto v : mp[u])
{
if (v == fa)
continue;
if (dfs(v, u))
f = 1;
}
if (u == goal)
f = 1;
return vis[u] = f;
}
void dfs2(int u, int fa)
{
for (auto v : mp[u])
if (vis[v] && v != fa)
dfs2(v, u);
for (auto v : mp[u])
{
if (v == fa || vis[v])
continue;
dfs2(v, u);
}
vis2[NUMS[u]] = 1;
if (vis[u])
{
while (vis2[MEX])
++MEX;
mex[u] = MEX;
}
}
vector<int> smallestMissingValueSubtree(vector<int> &parents, vector<int> &nums)
{
int n = parents.size();
N = n;
mex = vector<int>(n, 1);
vis = vector<int>(n + 5, 0);
goal = -1;
for (int i = 0; i < n; i++)
if (nums[i] == 1)
goal = i;
if (goal == -1) //不存在1 那么所有子树的mex都是1
return mex;
for (int i = 0; i < n; i++)
{
int fa = parents[i];
if (fa == -1)
continue;
mp[i].emplace_back(fa);
mp[fa].emplace_back(i);
}
dfs(0, -1); //找到权值为1的点到根结点的路径
NUMS = nums;
MEX = 1;
vis2 = vector<int>(n + 5, 0);
dfs2(0, -1);
return mex;
}
};
Solution III
根据dfs序重新标号。用线段树维护区间mex。代码有空再补。
以上是关于力扣2003 三种解法 启发式合并线段树维护mex运算思维的主要内容,如果未能解决你的问题,请参考以下文章