bitset + meet in the middle,P3067 [USACO12OPEN] Balanced Cow Subsets G

目录

一、题目

1、题目描述

2、输入输出

2.1输入

2.2输出

3、原题链接

二、解题报告

1、思路分析

2、复杂度

3、代码详解


一、题目

1、题目描述

编辑

2、输入输出

2.1输入

编辑

2.2输出

编辑

3、原题链接

https://www.luogu.com.cn/problem/P3067


二、解题报告

1、思路分析

洛谷题解区要么就是 O(3^n) 的错误做法,要么就是 O(6^{n / 2}) 的做法

这里给一种比较稳的做法

考虑一个平衡子集 S,它可以被分为4部分:

  1. s1:在 a 的前半部分中属于A 的集合

  2. s2:在 a 的前半部分中属于B 的集合

  3. s3:在 a 的后半部分中属于A 的集合

  4. s4:在 a 的后半部分中属于B 的集合

那么一个合法子集一定满足:s1 + s3 = s2 + s4 => s1 - s2 = s4 - s3

那么我们折半枚举前后两部分所有子集的 sum|A'| - sum|B'|

对于前半部分,我们枚举所有子集,保存所有 <sum|A'| - sum|B'|, msk> 这样的二元数对(其中msk为选择的下标集合),然后按照第一维排序并按照二元数对去重

然后按 第一维分组,用bitset 合并msk,保存在列表 st中

对于后半部分,我们枚举子集s,计算出sum|A'| - sum|B'|后,在st中二分得到对应的msk,和子集s的bitset合并

对于后半部分的子集s,其 bitset 中1的个数就是前半部分和s合并后是平衡子集的合法子集个数

值得注意的是,我们折半枚举子集的时候把空集也包含上,这样计算出来的答案会多1,输出的时候减去即可。

2、复杂度

时间复杂度: O(3 ^ {n/2} * (log(3^{n/2}) + 2^{n/2} / 64))空间复杂度:O(3^{n / 2} * 32)

3、代码详解

复制代码
  
cpp 复制代码
#include <bits/stdc++.h>
namespace ranges = std::ranges;
namespace views = std::views;
using i64 = long long;
using u32 = unsigned;
using u64 = unsigned long long;

constexpr int B = 1024;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n;
    std::cin >> n;
    const int U = 1 << (n / 2);
    std::vector<int> a(n / 2), b(n - n / 2), sum(U);
    for (int i = 0; i < n / 2; ++i) {
        std::cin >> a[i];        
        sum[1 << i] = a[i];
    }
    for (int i = 0; i < n - n / 2; ++i) {
        std::cin >> b[i];
    }

    std::vector<std::array<int, 2>> p;
    for (int s = 0; s < U; ++s) {
        if (s > 0) sum[s] = sum[s & (s - 1)] + sum[s & -s]; 
        p.push_back({sum[s], s});
        for (int t = s; t > 0; t = (t - 1) & s) {
            p.push_back({sum[s] - sum[t] * 2, s});
        }
    }
    ranges::sort(p);
    p.resize(std::unique(p.begin(), p.end()) - p.begin());
    
    std::vector<std::pair<int, std::bitset<B>>> st;
    for (int i = 0, j = 0; i < p.size(); i = j) {
        std::bitset<B> msk;
        while (j < p.size() && p[i][0] == p[j][0]) {
            msk.set(p[j++][1]);
        }
        st.push_back({p[i][0], msk});
    }

    const int V = 1 << b.size();
    std::vector<std::bitset<B>> good(V); 
    std::vector<int> nsum(V);
    for (int i = 0; i < b.size(); ++i) {
        nsum[1 << i] = b[i];
    }
    
    for (int s = 0; s < V; ++s) {
        if (s > 0) nsum[s] = nsum[s & (s - 1)] + nsum[s & -s];
        
        int target = nsum[s]; 
        auto it = ranges::lower_bound(st, target, {}, [](auto &x) {
            return x.first;
        });
        if (it != st.end() && it->first == target) {
            good[s] |= it->second;
        }
        
        for (int t = s; t > 0; t = (t - 1) & s) {
            target = nsum[s] - nsum[t] * 2; 
            it = ranges::lower_bound(st, target, {}, [](auto &x) {
                return x.first;
            });
            if (it != st.end() && it->first == target) {
                good[s] |= it->second;
            }
        }
    }

    int ans = 0;
    for (auto &bt : good) {
        ans += bt.count();
    }   
    
    std::cout << ans - 1 << '\n';

    return 0;
}
相关推荐
四处炼丹2 小时前
OpenClaw本地部署与Multi-Agent 技术分享
人工智能·算法·aigc·agent·ai编程
Σίσυφος19002 小时前
周期 Pattern Removal 算法
算法
飞Link2 小时前
深度捕捉时序本质:TSTD 异常检测之表示学习(Representation-based)全解析
学习·算法·数据挖掘·回归
滴滴答滴答答2 小时前
机考刷题之 10 LeetCode 200 岛屿数量
算法·leetcode·职场和发展
mit6.8243 小时前
Agent memory发展路线
算法
青桔柠薯片3 小时前
Linux I/O多路复用:深入浅出poll与epoll
linux·运维·服务器·算法
哈哈很哈哈3 小时前
逻辑回归Logistic Regression
算法·机器学习·逻辑回归
甄心爱学习3 小时前
【极大似然估计/最大化后验】为什么逻辑回归要使用交叉熵损失函数
算法·机器学习·逻辑回归
郝学胜-神的一滴4 小时前
深度学习入门全解析:从核心概念到实战基础 | 技术研讨会精华总结
人工智能·python·深度学习·算法·cnn