AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

【网络流/最小割/最大权值闭合子图】CF EDUdiv2.171-E

作者: 作者的头像   yankai ,  2024-10-30 22:29:24 ,  所有人可见 ,  阅读 9


1


https://codeforces.com/contest/2026/problem/E
最大权值闭合子图:有一个图,里面的点权值或正或负或零。需要找出一个权值最大的子图,且使得子图中每个点的后继都在子图中。
$$权值和=所有正值之和 - 未选择的正值之和 + 选中的负值之和$$
假设我们一开始拥有所有正值,当我们在此基础上不选择一个正值时,我们需要支付其权值的惩罚,当我们选择一个负值时,我们需要支付其权值的代价。当我们选择一个值时,必须选中其后继。
即最小化:不选择的正值 + 选中的负值的绝对值。代入最小割模型,一方面我们需要让这两部分为割,另一方面,对于点与点直接的边,因为必须选择,我们需要将其置为inf,使得其在最小割中一定不成为割边。
于是建模:设一个源点s,与所有的正值相连,边权为其点权。设一个汇点t,由所有的负值点指向,边权为其点权的绝对值。
这样当一个正值点不被选择时,它一定在T中,s与其的连边成为割边。当一个负值点被选择时,它一定在s中,它与t点的连边成为割边。此时求一个最小割,即能最大化所选的权值和。

E题题解

如果选了一个数,可以获得价值1,但是必须要支付其二进制所含1数量的价值(重复的则可以不支付),因此原数组的点可以视作权值1的点,设一个源点s指向。每个二进制位可以视作权值-1的点,指向一个汇点t。原数组点i与其对应位为1的二进制位连线,边权为INF。答案为n-最小割。

#include<bits/stdc++.h>

#define MULTI_TEST

using namespace std;
using ll = long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;

constexpr ll inf = 1E9;
template<class T>
struct MaxFlow {
    struct _Edge {
        int to;
        T cap;
        _Edge(int to, T cap) : to(to), cap(cap) {}
    };

    int n;
    std::vector<_Edge> e;
    std::vector<std::vector<int>> g;
    std::vector<int> cur, h;

    MaxFlow() {}
    MaxFlow(int n) {
        init(n);
    }

    void init(int n) {
        this->n = n;
        e.clear();
        g.assign(n, {});
        cur.resize(n);
        h.resize(n);
    }

    bool bfs(int s, int t) {
        h.assign(n, -1);
        std::queue<int> que;
        h[s] = 0;
        que.push(s);
        while (!que.empty()) {
            const int u = que.front();
            que.pop();
            for (int i : g[u]) {
                auto [v, c] = e[i];
                if (c > 0 && h[v] == -1) {
                    h[v] = h[u] + 1;
                    if (v == t) {
                        return true;
                    }
                    que.push(v);
                }
            }
        }
        return false;
    }

    T dfs(int u, int t, T f) {
        if (u == t) {
            return f;
        }
        auto r = f;
        for (int &i = cur[u]; i < int(g[u].size()); ++i) {
            const int j = g[u][i];
            auto [v, c] = e[j];
            if (c > 0 && h[v] == h[u] + 1) {
                auto a = dfs(v, t, std::min(r, c));
                e[j].cap -= a;
                e[j ^ 1].cap += a;
                r -= a;
                if (r == 0) {
                    return f;
                }
            }
        }
        return f - r;
    }
    void addEdge(int u, int v, T c) {
        g[u].push_back(e.size());
        e.emplace_back(v, c);
        g[v].push_back(e.size());
        e.emplace_back(u, 0);
    }
    T flow(int s, int t) {
        T ans = 0;
        while (bfs(s, t)) {
            cur.assign(n, 0);
            ans += dfs(s, t, std::numeric_limits<T>::max());
        }
        return ans;
    }

    std::vector<bool> minCut() {
        std::vector<bool> c(n);
        for (int i = 0; i < n; i++) {
            c[i] = (h[i] != -1);
        }
        return c;
    }

    struct Edge {
        int from;
        int to;
        T cap;
        T flow;
    };
    std::vector<Edge> edges() {
        std::vector<Edge> a;
        for (int i = 0; i < e.size(); i += 2) {
            Edge x;
            x.from = e[i + 1].to;
            x.to = e[i].to;
            x.cap = e[i].cap + e[i + 1].cap;
            x.flow = e[i + 1].cap;
            a.push_back(x);
        }
        return a;
    }
};

int n;
void solve(){
    cin>>n;
    MaxFlow<int> mf(n+62);
    int s = n+60, t = n+61;
    for(int i=0;i<n;i++){
        mf.addEdge(s,i,1);
        ll x;
        cin>>x;
        bitset<60> b(x);
        for(int j=0;j<60;j++){
            if(b[j]==1)
                mf.addEdge(i,n+j,inf);
        }
    }
    for(int i=0;i<60;i++) mf.addEdge(n+i,t,1);
    cout<<n - mf.flow(s,t)<<endl;

}

int main(){     
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
    std::ios::sync_with_stdio(0);std::cout.tie(0);std::cin.tie(0);
    int T = 1;
#ifdef MULTI_TEST
    cin>>T;
#endif
    while(T--){
        solve();
    }
    return 0;
}

0 评论

App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息