2020杭电多校(二) New Equipments(最小费用最大流)

New Equipments

思路

数据已经有提示了 b b < = 4 a c b * b <= 4 * a * c ,这意味着,每一个 a , b , c a, b, c 构成的二元一次方程只与 x x 坐标最多相交一次,所以我们对每一个 a i i + b i + c = y a * i * i + b * i + c = y ,在 x x 坐标上对应的 i i ,只有唯一最值,因此我们只要对每一个方程,在它的对称轴两侧选点即可。

问题是如何来维护这个最大值呢,显然的每一个方程只能对应一个 x x 轴上的点,这就有点像二分图了,这里无非就是加一个 c o s t cost 边权值嘛,所以我们只要建一个带权的二分图匹配,跑一个最小费用最大流不就行了。

代码

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

inline ll read() {
    ll f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-')    f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f * x;
}

const int N1 = 2e5 + 10, N2 = 2e5 + 10;
const int INF = 0x3f3f3f3f;

int head[N1], to[N2], nex[N2], cap[N2], cnt;
ll value[N2], a[N1], b[N1], c[N1], dis[N1];

int pre[N1], id[N1], flow[N1], visit[N1], n, m, s, t;

void add(int x, int y, int f, ll w) {
    to[cnt] = y;
    nex[cnt] = head[x];
    value[cnt] = w;
    cap[cnt] = f;
    head[x] = cnt++;
}

bool spfa() {
    memset(visit, 0, sizeof visit);
    memset(dis, 0x3f, sizeof dis);
    queue<int> q;
    q.push(s);
    dis[s] = 0, visit[s] = 1, flow[s] = INF, pre[t] = -1;
    while(!q.empty()) {
        int temp = q.front();
        q.pop();
        visit[temp] = 0;
        for(int i = head[temp]; ~i; i = nex[i]) {
            if(cap[i] > 0 && dis[to[i]] > dis[temp] + value[i]) {
                dis[to[i]] = dis[temp] + value[i];
                flow[to[i]] = min(flow[temp], cap[i]);
                pre[to[i]] = temp;
                id[to[i]] = i;
                if(!visit[to[i]]) {
                    q.push(to[i]);
                    visit[to[i]] = 1;
                }
            }
        }
    }
    return  pre[t] != -1;
}

int min_cost_and_max_flow() {
    vector<ll> ans;
    ll now = 0;
    while(spfa()) {
        now += flow[t] * dis[t];
        ans.push_back(now);
        int p = t;
        while(p != s) {
            cap[id[p]] -= flow[t];
            cap[id[p] ^ 1] += flow[t];
            p = pre[p];
        }
    }
    for(int i = 0; i < ans.size(); i++) {
        printf("%lld%c", ans[i], i + 1 == ans.size() ? '\n' : ' ');
    }
}

int point[N1], tot;

void init() {
    memset(head, -1, sizeof head);
    cnt = tot = 0;
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int _ = read();
    for(int cas = 1; cas <= _; cas++) {
        init();
        n = read(), m = read();
        init();
        s = 0;
        for(int i = 1; i <= n; i++) {
            add(s, i, 1, 0);//源点向每个人建边,
            add(i, s, 0, 0);
            a[i] = read(), b[i] = read(), c[i] = read();
            int mid = -(b[i] / (2 * a[i]));
            mid = max(1, mid);
            mid = min(mid, m);
            for(int j = mid, sum = 1; j >= 1 && sum <= n; j--, sum++) point[++tot] = j;
            for(int j = mid + 1, sum = 1; j <= m && sum <= n; j++, sum++) point[++tot] = j;
        }
        sort(point + 1, point + 1 + tot);
        tot = unique(point + 1, point + 1 + tot) - (point + 1);
        t = n + tot + 1;
        for(int j = 1; j <= tot; j++) {
            add(j + n, t, 1, 0);//机器向汇点建边。
            add(t, j + n, 0, 0);
        }
        for(int i = 1; i <= n; i++) {//每个人跟机器连边
        	int mid = -(b[i] / (2 * a[i]));
            mid = max(1, mid);
            mid = min(mid, m);
            for(int j = mid, sum = 1; j >= 1 && sum <= n; j--, sum++) {
            	add(i, (lower_bound(point + 1, point + 1 + tot, j) - point) + n, 1, a[i] * j * j + b[i] * j + c[i]);
                add((lower_bound(point + 1, point + 1 + tot, j) - point) + n, i, 0, -(a[i] * j * j + b[i] * j + c[i]));
			} 
            for(int j = mid + 1, sum = 1; j <= m && sum <= n; j++, sum++) {
                add(i, (lower_bound(point + 1, point + 1 + tot, j) - point) + n, 1, a[i] * j * j + b[i] * j + c[i]);
                add((lower_bound(point + 1, point + 1 + tot, j) - point) + n, i, 0, -(a[i] * j * j + b[i] * j + c[i]));
            }
        }
        min_cost_and_max_flow();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45483201/article/details/107607488