zoukankan      html  css  js  c++  java
• # 2020杭电多校第二场 E

### 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) {
}
int dinic_spfa() {
qtail = -1;
memset(vis, 0x00, sizeof vis);
memset(dist, 0x3f, sizeof dist);
vis[s] = 1;
dist[s] = 0;
qu[++qtail] = s;
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;

{
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();
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
*/

• 相关阅读:
作业帮：最长连续序列(头部插入)
作业帮：字符串反转(头部插入)
作业帮：给定一个整数数组，找出其中两个数相加等于目标值（去重set）
JVM系列之七：HotSpot 虚拟机
JVM系列之六：内存溢出、内存泄漏 和 栈溢出
JVM系列之四：运行时数据区
JVM系列之五：垃圾回收
JVM系列之三：类装载器子系统
JVM系列之二：编译过程
JVM系列之一：JVM架构
• 原文地址：https://www.cnblogs.com/mollnn/p/13382016.html