zoukankan      html  css  js  c++  java
  • [CF1446C] Xor Tree

    Description

    给定 (n) 个自然数,每个数会与自己异或最小的数,向它连一条边。求至少删去多少个数,才能使这个自然数集合经过这样的操作构成的图是一棵树。边都是无向的,重边自动合并。

    Solution

    考虑在 0-1 Trie 中,如果结点 (p) 的子树中有 (1) 个有效叶子结点,那么这个结点一定会与子树外的结点连边,因此我们可以说 (p) 的子树是安全的。

    相反地,如果 (p) 的子树内有 (ge 2) 个有效叶子结点,那么这些结点之间一定会相互匹配连边,因此 (p) 子树内的这些节点就会与剩余的部分断开。

    因此,对于一个结点 (p),它的左子树和右子树分别为 (x,y),如果两边的叶子结点数目都大于 (1),那么我们需要将一边的有效叶子结点数目删减到不超过 (1),才能使得整个过程是有效的。至于删除哪一边,需要递归做下去。

    具体地,我们定义 (f(p)) 表示结点 (p) 的子树内的有效叶子结点最多可以保留多少个。

    如果 (p) 的左子树中的有效叶子结点个数为零,那么 (f(p) = f(p.rightChild))

    如果 (p) 的右子树中的有效叶子结点个数为零,那么 (f(p) = f(p.leftChild))

    如果 (p) 的左右子树中的有效叶子结点个数都非零,那么 (f(p) = max(f(p.leftChild), p.(rightChild)) + 1),即我们将一边删除到只剩下一个。

    作为递归的边界,当子树内只有一个结点(或者层数达到叶子)的时候,返回一个值即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long
    const int N = 1000005;
    
    int n;
    vector<int> a;
    
    int f(vector<int> a, int b)
    {
        if (b == -1)
            return a.size() ? 1 : 0;
        vector<int> c[2];
        for (int i : a)
            c[(i >> b) & 1].push_back(i);
        if (c[0].size() == 0)
            return f(c[1], b - 1);
        if (c[1].size() == 0)
            return f(c[0], b - 1);
        return max(f(c[0], b - 1), f(c[1], b - 1)) + 1;
    }
    
    signed main()
    {
        ios::sync_with_stdio(false);
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            int t;
            cin >> t;
            a.push_back(t);
        }
        cout << n - f(a, 30) << endl;
    }
    
    
  • 相关阅读:
    通过存储过程的游标修改某个字段的全部数据
    spring cloud配置注册中心显示服务的ip地址和端口
    git几个必知托管平台
    hdu5790
    hdu5794
    hdu5739
    hdu5829
    线性规划初探
    bzoj4199
    bzoj4197
  • 原文地址:https://www.cnblogs.com/mollnn/p/14013000.html
Copyright © 2011-2022 走看看