力扣2003 三种解法 启发式合并、线段树维护mex运算、思维

题目链接

2003. 每棵子树内缺失的最小基因值

给出一颗有根树,每个节点有一个权值 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。代码有空再补。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值