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;
}
相关推荐
如何原谅奋力过但无声12 小时前
【灵神高频面试题合集09-13】二叉树、二叉搜索树
数据结构·算法·leetcode
皆圥忈12 小时前
磁盘物理结构与文件系统基础讲解
linux·算法
数据仓库搬砖人12 小时前
用 LangGraph 从零搭一个客服 Agent:多轮对话 + 工具调用全流程
算法
GuWenyue12 小时前
告别JS类型坑!Ts为什么在ai时代逐渐成为"第一"语言
前端·算法·typescript
子琦啊12 小时前
哈希与前缀和
算法·哈希算法
Deep-w12 小时前
【MATLAB】基于离散 LQR 的车辆横向轨迹跟踪控制方法研究
开发语言·算法·matlab
Peter·Pan爱编程12 小时前
23. 算法库:用算法代替手写循环
c++·人工智能·算法
小欣加油13 小时前
leetcode2161 根据给定数字划分数组
数据结构·c++·算法·leetcode·职场和发展
Momo__zz13 小时前
零代码平台设计
算法·深度优先
cpp_250113 小时前
P2947 [USACO09MAR] Look Up S
数据结构·c++·算法·题解·单调栈·洛谷