【CLYZ集训】变量取值【网络流】

在这里插入图片描述

思路:

容易把答案转化为 ∑ s i w i + ∑ t i ( w x − w y ) \sum{s_iw_i}+\sum{t_i(w_x-w_y)} siwi+ti(wxwy),然后我们设初始代价为 − ∑ ∣ s i ∣ w i -\sum{|s_i|w_i} siwi,然后考虑建模。
如果Si大于0,则源点向i连一条流量为2SiW的边,代表割掉这条边(也就是让i为w)要多付出的代价,向汇点连0的边。Si小于0,汇点向i连2SiW的边,向汇点连0的边。
然后对于wx-wy,则直接连2wti的双向边。
考虑限制,若果是<,则强制x和y的w,分别连inf边。
如果是=,则之间连inf边
如果是<=,则y向x连inf边。
然后做最小割就是答案。

c o d e code code

#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
#include <cstring>
#define ll long long


using namespace std;

const ll MAXN = 550, inf = 1e12;

ll Q, tot;
ll n, w, p, q, S, T;
ll s[MAXN * 3], a[MAXN * 3];
ll crn[MAXN * 3], dep[MAXN * 3], head[MAXN * 3];
struct edge {
    ll to, next, w, op;
} b[MAXN * MAXN * 21];
ll MB[MAXN][MAXN];
queue<ll> G;

void add(ll x, ll y, ll w) {
    b[++tot] = (edge){ y, head[x], w, tot + 1 };
    head[x] = tot;
    b[++tot] = (edge){ x, head[y], 0, tot - 1 };
    head[y] = tot;
}

bool bfs() {
    for (int i = 1; i <= T; i++) crn[i] = head[i], dep[i] = 0;
    while (!G.empty()) G.pop();
    G.push(S);
    dep[S] = 1;
    while (!G.empty()) {
        int now = G.front();
        G.pop();
        for (int i = head[now]; i; i = b[i].next)
            if (b[i].w && !dep[b[i].to]) {
                dep[b[i].to] = dep[now] + 1;
                if (b[i].to == T)
                    return 1;
                G.push(b[i].to);
            }
    }
    return 0;
}
/*
ll dfs(ll x, ll flo) {
        if(x == T) return flo;
        ll d = flo;
        for(ll i = crn[x]; i; i = b[i].next) {
                crn[x] = b[i].next;
                ll y = b[i].to;
                if(dep[y] == dep[x] + 1 && b[i].w > 0) {
                        ll tmp = dfs(y, min(d, b[i].w));
                        b[i].w -= tmp;
                        b[i ^ 1].w += tmp;
                        d -= tmp;
                        if(d == 0) break;
                }
        }
        return flo - d;
}
*/

ll dfs(int now, ll sum) {
    if (now == T)
        return sum;
    ll go = 0;
    for (int i = crn[now]; i; i = b[i].next)
        if (b[i].w && dep[now] + 1 == dep[b[i].to]) {
            ll this_go = dfs(b[i].to, min(sum - go, b[i].w));
            if (this_go) {
                b[i].w -= this_go;
                b[b[i].op].w += this_go;
                go += this_go;
                if (go == sum)
                    return go;
            }
        }
    dep[now] = -1;
    return go;
}

ll dinic() {
    ll ans = 0;
    while (bfs()) {
        ans += dfs(S, 1e18);
    }
    return ans;
}

void clean() {
    tot = 0;
    memset(head, 0, sizeof(head));
    memset(MB, 0, sizeof(MB));
    memset(b, 0, sizeof(b));
    memset(s, 0, sizeof(s));
}

int main() {
    freopen("variable.in", "r", stdin);
    freopen("variable.out", "w", stdout);
    scanf("%lld", &Q);
    while (Q--) {
        scanf("%lld%lld%lld%lld", &n, &w, &p, &q);
        for (ll i = 1; i <= n; i++) s[i] = 1;
        S = n + 1, T = n + 2;
        for (ll i = 1; i <= p; i++) {
            ll r1, r2, r3, r4, r5, r6, r7, r8, r9;
            scanf("%lld%lld%lld%lld%lld%lld%lld%lld%lld", &r1, &r2, &r3, &r4, &r5, &r6, &r7, &r8, &r9);
            MB[r1][r2] += r4;
            MB[r2][r3] += r5;
            MB[r3][r1] += r6;
            s[r1] += r7 - r9, s[r2] += r8 - r7, s[r3] += r9 - r8;
        }
        ll low = 0;
        for (ll i = 1; i <= n; i++) {
            low += abs(s[i]);
            if (s[i] > 0)
                add(S, i, 2 * s[i]);
            if (s[i] < 0)
                add(i, T, -2 * s[i]);
            for (int j = i + 1; j <= n; j++) {
                if (MB[i][j] + MB[j][i])
                    add(i, j, MB[i][j] * 2 + MB[j][i] * 2), add(j, i, MB[i][j] * 2 + MB[j][i] * 2);
            }
        }
        for (ll i = 1; i <= q; i++) {
            ll r1, r2, r3;
            scanf("%lld%lld%lld", &r1, &r2, &r3);
            if (r3 == 0)
                add(r2, r1, inf);
            if (r3 == 1)
                add(r1, r2, inf), add(r2, r1, inf);
            if (r3 == 2)
                add(S, r1, inf), add(r2, T, inf);
        }
        printf("%lld\n", dinic() * w - low * w);
        clean();
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值