2020杭电多校第二场 E - New Equipments - 费用流

Description

工厂中有 \(n\) 个工人,\(m\) 个设备,第 \(i\) 个工人匹配第 \(j\) 号设备的代价是 \(a_i j^2 + b_i j + c_i\)

每个工人和设备至多只能参与一次匹配

对每一个 \(k \in [1,n]\),求形成 \(k\) 对匹配的最小代价。

Solution

对于每个二次函数,其最小值点唯一,显然这个工人取的一定是二次函数前 \(n\) 小的整点

于是每个工人向前 \(n\) 小的位置连边,即可建出一张点数边数均为 \(O(n^2)\) 的费用流图

增广 \(n\) 次找到流量为 \(1,2,3,...,n\) 的费用流即可

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1005;

namespace flow {
const int N = 100005;
const int M = 1000005;
const int inf = 1e+12;
struct Edge {
    int p, c, w, nxt = -1;
} e[N];
int s, t, tans, ans, cost, ind, bus[N], qhead = 0, qtail = -1, qu[M],vis[N], dist[N];

void graph_link(int p, int q, int c, int w) {
    e[ind].p = q;
    e[ind].c = c;
    e[ind].w = w;
    e[ind].nxt = bus[p];
    bus[p] = ind;
    ++ind;
}
void make(int p, int q, int c, int w) {
    graph_link(p, q, c, w);
    graph_link(q, p, 0, -w);
}
int dinic_spfa() {
    qhead = 0;
    qtail = -1;
    memset(vis, 0x00, sizeof vis);
    memset(dist, 0x3f, sizeof dist);
    vis[s] = 1;
    dist[s] = 0;
    qu[++qtail] = s;
    while (qtail >= qhead) {
        int p = qu[qhead++];
        vis[p] = 0;
        for (int i = bus[p]; i != -1; i = e[i].nxt)
            if (dist[e[i].p] > dist[p] + e[i].w && e[i].c > 0) {
                dist[e[i].p] = dist[p] + e[i].w;
                if (vis[e[i].p] == 0)
                    vis[e[i].p] = 1, qu[++qtail] = e[i].p;
            }
    }
    return dist[t] < inf;
}
int dinic_dfs(int p, int lim) {
    if (p == t)
        return lim;
    vis[p] = 1;
    int ret = 0;
    for (int i = bus[p]; i != -1; i = e[i].nxt) {
        int q = e[i].p;
        if (e[i].c > 0 && dist[q] == dist[p] + e[i].w && vis[q] == 0) {
            int res = dinic_dfs(q, min(lim, e[i].c));
            cost += res * e[i].w;
            e[i].c -= res;
            e[i ^ 1].c += res;
            ret += res;
            lim -= res;
            if (lim == 0)
                break;
        }
    }
    return ret;
}
void solve(int _s,int _t,int times) {
    s=10003; t=_t;
    ans = 0;
    cost = 0;
    for(int i=1;i<=times;i++) {
        make(s,10001,1,0);
        dinic_spfa();
        memset(vis, 0x00, sizeof vis);
        ans += dinic_dfs(s, inf);
        cout<<cost<<(i==times?"":" ");
    }
    cout<<endl;
}
void init() {
    memset(bus, 0xff, sizeof bus);
    ind=0;
}
}

//// Main

struct network_edge
{
    int u,v,w,c;
};

vector <network_edge> network_edges;

void make(int u,int v,int w,int c)
{
    network_edges.push_back({u,v,w,c});
}

void network_print()
{
    for(auto p:network_edges)
    {
        cout<<p.u<<","<<p.v<<"  "<<p.w<<"  "<<p.c<<endl;
    }
}

void network_transfer()
{
    for(auto p:network_edges)
    {
        flow::make(p.u,p.v,p.w,p.c);
    }
}

struct equip
{
    int a,b,c;
} equipment[N];

int n,m;

void clean()
{
    network_edges.clear();
    flow::init();
}

const int S = 10001;
const int T = 10002;

void load()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        equip &e=equipment[i];
        cin>>e.a>>e.b>>e.c;
    }
}

int get_minpoint(int a,int b,int c)
{
    return -b/(2*a);
}

void build_network()
{
    map <int,int> mp;

    for(int i=1;i<=n;i++)
    {
        equip &e=equipment[i];
        int p=get_minpoint(e.a,e.b,e.c);

        p=min(p,m);
        p=max(p,1ll);

        int low=max(1ll,p-n-1);
        int high=min(m,p+n+1);

        for(int j=low;j<=high;j++)
        {
            mp[j]++;
        }
    }

    int ind=0;
    for(auto &pr:mp)
    {
        pr.second=++ind;
    }

    for(int i=1;i<=n;i++)
    {
        equip &e=equipment[i];
        int p=get_minpoint(e.a,e.b,e.c);

        p=min(p,m);
        p=max(p,1ll);

        int low=max(1ll,p-n-1);
        int high=min(m,p+n+1);

        for(int j=low;j<=high;j++)
        {
            make(i,1000+mp[j],1,e.a*j*j+e.b*j+e.c);
        }
    }

    for(int i=1;i<=n;i++)
    {
        make(S,i,1,0);
    }

    for(int i=1;i<=ind;i++)
    {
        make(1000+i,T,1,0);
    }
}

void solve()
{
    flow::solve(S,T,n);
}

void work()
{
    clean();
    load();
    build_network();
    //network_print();
    network_transfer();
    solve();
}

signed main()
{
    ios::sync_with_stdio(false);

    int t;
    cin>>t;
    while(t--)
    {
        work();
    }
}

/*
2
3 5
2 3 10
2 -3 10
1 -1 4
3 5
2 3 10
2 -3 10
1 -1 4
*/

猜你喜欢

转载自www.cnblogs.com/mollnn/p/13382016.html