• AtCoder Beginner Contest 252


    Ex K-th Beautiful Necklace

    题意

    有 N 个石头,每个石头有不同的颜色和价值
    颜色一共有 C 种,每种颜色至少存在一个石头
    可以选择一些石头串成一条项链,项链的价值是所有石头价值的异或和
    从 N 个石头中选择 C 个石头串一串项链,要求每个石头的颜色都不同
    问所有的串法中,价值第 K 大的项链的价值是多少

    分析

    C 的最大值是70,假设每个颜色的石头都有2个或3个,那么可选的方案最大值为 \(2^{35}\) 或 \(3^{22} * 4\)
    所以暴力枚举不可取
    可以进行 meet-in-the-middle 处理,石头个数为 C 的项链可以由2串石头个数为 C/2 的项链拼接得到
    假设前一串项链为 a,后一串项链为 b,从 a 中选择一串项链 x,求 b 中有多少串项链满足与 x 的异或大于某个值
    01 字典树可以满足这个需求,对一个数字 x 求树中与 x 的异或值大于某个数 y 的数字的个数可以在 \( \log{y}\) 时间内求得
    那么可以对答案进行二分,先用 b 构建字典树,然后对 a 中每个数 x 进行枚举,求字典树中大于 x 的数字的个数,总和与 K 相比
    时间复杂度为 \( O(s\log^{2}V) \),s 为 a 中项链的总数,V 为项链价值的最大值
    这个时间复杂度无法接受
    考虑另一种枚举方式,从答案的最高位开始枚举
    考虑当前位为 1,然后枚举 a 的每个数字 x,可以在字典树中求出 考虑了前面所有位之后 与 x 异或后当前位为 1 的数字的个数,总和为 m
    如果 m 大于等于 K,说明第 K 大的数字是在这 m 个数字中,所以答案的当前位应该取 1
    如果 m 小于 K,说明第 K 大的数字不在这 m 个数字中,要更小,所以答案的当前位取 0
    对于 x 来说,考虑到了第 i 位,那么就对应这个数字在字典树中的第 i-1 层到第 i 层的走向
    所以当前位确定后需要对 x 的在字典树中的位置进行更新,根据答案的当前位和 x 的当前位决定沿字典树中0的边更新还是1的边更新
    对于 m 小于 K 的情况,就是在剩下的数字中考虑第 K - m 大的数字,所以答案考虑当前位为1的时候需要 K -= m
    总的时间复杂度为 \( s\log{V} \)

    code

    #include <bits/stdc++.h>
    using namespace  std;
    typedef long long ll;
    typedef pair<int, int> pii;
    
    const int N = 8e5 + 5;
    const int mdep = 60;
    int son[N*mdep][2];
    int tot[N*mdep];
    int nn = 1;
    int cur[N];
    vector<ll> color[75];
    void insert(const ll x) {
        int cn = 1;
        tot[cn]++;
        for (int i = mdep;i >= 0;i--) {
            int cb = x >> i & 1LL;
            if (!son[cn][cb]) {
                son[cn][cb] = ++nn;
            }
            cn = son[cn][cb];
            tot[cn]++;
        }
    }
    
    int main() {
        int n,c;
        ll k;
        cin >> n >> c >> k;
        ll mul = 1;
        for (int i = 0;i < n;i++) {
            int col;
            ll val;
            cin >> col >> val;
            color[col].push_back(val);
        }
        for (int i = 1;i <= c;i++) {
            if (color[i].empty()) {
                continue;
            }
            mul *= (ll)color[i].size();
        }
        int max_col = 1;
        ll mul1 = 1;
        for (int i = 1;i <= c;i++) {
            if (!color[i].empty()) {
                mul1 *= color[i].size();
                if (mul / mul1 <= mul1) {
                    max_col = i;
                    break;
                }
            }
        }
        // meet in the middle
        vector<ll> tree_num;
        for (int i = max_col + 1;i <= c;i++) {
            if (color[i].empty()) {
                continue;
            }
            if (tree_num.empty()) {
                tree_num.swap(color[i]);
            } else {
                vector<ll> temp;
                for (const ll x : tree_num) {
                    for (const ll y : color[i]) {
                        temp.push_back(x ^ y);
                    }
                }
                tree_num.swap(temp);
            }
        }
        vector<ll> ntr_num;
        for (int i = 1;i <= max_col;i++) {
            if (color[i].empty()) {
                continue;
            }
            if (ntr_num.empty()) {
                ntr_num.swap(color[i]);
            } else {
                vector<ll> temp;
                for (const ll x : ntr_num) {
                    for (const ll y : color[i]) {
                        temp.push_back(x ^ y);
                    }
                }
                ntr_num.swap(temp);
            }
        }
    
        if (tree_num.empty()) {
            sort(ntr_num.begin(), ntr_num.end(), greater<>());
            cout << ntr_num[k-1] << endl;
        } else {
            ll ans = 0;
            // build 01-trie
            for (const ll x : tree_num) {
                insert(x);
            }
            for (int i = 0;i < ntr_num.size();i++) {
                cur[i] = 1;
            }
            for (int i = mdep;i >= 0;i--) {
                ll sum = 0;
                for (int j = 0;j < ntr_num.size();j++) {
                    int cb = ntr_num[j] >> i & 1LL;
                    int cn = cur[j];
                    // find how many numbers xor is 1 for current bit position
                    sum += tot[son[cn][cb^1]];
                }
                if (sum >= k) {
                    ans |= (1LL << i);
                    for (int j = 0;j < ntr_num.size();j++) {
                        // update x's position in 01-trie
                        int cb = ntr_num[j] >> i & 1LL;
                        int cn = cur[j];
                        cur[j] = son[cn][cb^1];
                    }
                } else if (sum < k) {
                    k -= sum;
                    for (int j = 0;j < ntr_num.size();j++) {
                        // update x's position in 01-trie
                        int cb = ntr_num[j] >> i & 1LL;
                        int cn = cur[j];
                        cur[j] = son[cn][cb];
                    }
                }
            }
            cout << ans << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_02 递归_2_练习_使用递归计算1-n之间的和
    阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_02 递归_1_递归概念&分类&注意事项
    阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_01 File类_8_File类遍历(文件夹)目录功能
    阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_01 File类_7_File类创建删除功能的方法
    阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_01 File类_6_File类判断功能的方法
    阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_01 File类_5_File类获取功能的方法
    阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_01 File类_4_File类的构造方法
    冒泡排序
    身份证验证合法性js--已验证
    演示工厂模式
  • 原文地址:https://www.cnblogs.com/kickit/p/16388333.html
Copyright © 2020-2023  润新知