Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • codeforses 题解

  • atcoder 题解

  • XCPC 题解

  • 校训练赛题解

  • 牛客题解

  • 蓝桥杯题解

  • 典题选讲

    • CF2025E Card Game (动态规划、多项式快速幂、银牌题)
    • CF2026E Best Subsequence(网络流、最大权闭合图,铜牌题)
    • 杂题

    • 算法题解
    • 典题选讲
    martian148
    2024-11-14
    目录

    CF2026E Best Subsequence(网络流、最大权闭合图,铜牌题)

    # CF2026E Best Subsequence(网络流、最大权闭合图,铜牌题)

    # Question

    给出一个数组 {a}\{a\}{a},选择一个子序列,使得子序列的数的数量 - 所有数的或最小

    # Solution

    这是一个最大权闭合图的典题,后面去学了一下

    我们把每个数看成一个节点,把每个 bit 看成一个节点

    • 起点向每个数建立一条边权为 111 的边
    • 如果数 xxx 的第 iii 为为 111,则 xxx 向第 iii 个 bit 建立一条边权为 INFINFINF 的边
    • 每个 bit 向终点建立一条边权为 111 的边

    则答案就是 n−n-n− 最大流

    如何理解:

    • 显然不能切边权为 INFINFINF 的边
    • 如果切了起点向每个数的边,那么说明不选取这个数
    • 如果切了每个 bit 向终点的边,说明这个 bit 选,也就是说,和这个 bit 连边的数可选可不选

    # Code

    #include <bits/stdc++.h>
    using namespace std;
    
    constexpr int INF = 0x3f3f3f3f;
    
    using ll = long long;
    
    struct Dinic {
        struct Edge {
            int from, to, cap, flow;
        };
        int n, m, s, t;
        vector<Edge> edges;
        vector<vector<int>> g;
        vector<int> d, cur; // d 为层次,cur 为当前弧优化
    
        void init (int n_) {
            n = n_; edges.clear();
            d.assign(n, 0);
            g.assign(n, vector<int>());
        }
    
        void add_e (int from, int to, int cap) {
            // cout << from << ' ' << to << ' ' << cap << '\n';
            edges.push_back(Edge{from, to, cap, 0});
            edges.push_back(Edge{to, from, 0, 0});
            m = edges.size();
            g[from].push_back(m - 2);
            g[to].push_back(m - 1);
        }
    
        bool bfs () {
            vector<int> vis (n, 0);
            queue<int> q; q.push(s); d[s] = 0; vis[s] = 1;
            while (!q.empty()) {
                int x = q.front(); q.pop();
                for (auto i : g[x]) {
                    Edge &e = edges[i];
                    if (vis[e.to] == 0 && e.cap > e.flow) {
                        vis[e.to] = 1;
                        d[e.to] = d[x] + 1;
                        q.push(e.to);
                    }
                }
            }
            return vis[t]; // 是否存在能到达汇点的路径
        }
    
        int dfs (int x, int a) { // a 表示从源点到 x 的可改进量
            if (x == t || a == 0) return a;
            int flow = 0, f;
            for (int &i = cur[x]; i < g[x].size(); i++) { // 当前弧优化,在 cur[x] 之前都没有增广成功
                Edge &e = edges[g[x][i]];
                if (d[x] + 1 == d[e.to] && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) {
                    e.flow += f;
                    edges[g[x][i] ^ 1].flow -= f;
                    flow += f;
                    a -= f;
                    if (a == 0) break;
                }
            }
            return flow;
        }
    
        int max_flow (int s, int t) {
            this->s = s; this->t = t;
            int flow = 0;
            while (bfs()) {
                cur.assign(n, 0);
                flow += dfs(s, INF);
            }
            return flow;
        }
    };
    
    
    void solve() {
        int n; cin >> n;
        vector<ll> a(n + 1);
        ll M = 0;
        for (int i = 1; i <= n; i++) cin >> a[i], M |= a[i];
        int cnt = __builtin_popcountll(M); // 1 的个数
    
        Dinic ek; ek.init(n + 60 + 3);
        int S = 0, T = n + 60 + 1, S_ = n + 60 + 2;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < 60; j++) if (a[i] >> j & 1) {
                ek.add_e(j + 1, i + 60, 1);
            }
        }
    
        for (int i = 1; i <= n; i++) 
            ek.add_e(i + 60, T, 1);
        
        for (int i = 1; i <= 60; i++)
            ek.add_e(S, i, 1);
    
        int ans = n - ek.max_flow(S, T);
        cout << ans << '\n';
    }
    
    int main() {
        std::ios::sync_with_stdio(false);
    
        int T; std::cin >> T;
        while (T--) solve();
        return 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    上次更新: 2025/04/08, 18:03:31
    CF2025E Card Game (动态规划、多项式快速幂、银牌题)

    ← CF2025E Card Game (动态规划、多项式快速幂、银牌题)

    最近更新
    01
    Java基础语法
    05-26
    02
    开发环境配置
    05-26
    03
    pink 老师 JavaScript 学习笔记
    05-26
    更多文章>
    Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式