原题链接
题面
题意
给定n个员工,每个员工配一台机器,每台机器都会产生不同的费用,费用由给定的函数产生。由函数图像可知,这个二元函数是一个开口向上的函数,并且位于y轴上方,因此最小值会在对称轴附近产生。如题中所给的m值范围在(-1e8 ~ 1e8)之间,因此必须要离散化才能存在下。
所以我们可以在对称轴附近找n个最接近的值,根据hall定理,自然能完全匹配。然后再将二分图的模型转换成网络流即可。
#include
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 3e3 + 10;
const int M = 5e5 + 10;
int h[N], last[N], pre[N], vis[N], flow[N];
ll ans[N], dis[N];
struct edge {
int to, next, cap;
ll cos;
}e[M<<1];
struct node {
ll a, b, c;
}p[55];
vector<int> a[55], b;
int n, m, cnt, s, t, k;
void add(int u, int v, int cap, ll cos) {
e[cnt].to = v;
e[cnt].cap = cap;
e[cnt].cos = cos;
e[cnt].next = h[u];
h[u] = cnt++;
e[cnt].to = u;
e[cnt].cap = 0;
e[cnt].cos = -cos;
e[cnt].next = h[v];
h[v] = cnt++;
}
bool spfa() {
queue<int> q;
memset(pre, -1, sizeof pre);
memset(vis, 0, sizeof vis);
memset(dis, 0x3f, sizeof dis);
dis[s] = 0;
vis[s] = 1;
flow[s] = INF;
q.push(s);
while (q.size()) {
int u = q.front();
q.pop();
vis[u] = 0;
for (int i = h[u]; ~i; i = e[i].next) {
int v = e[i].to;
if (e[i].cap && dis[v] > dis[u] + e[i].cos) {
pre[v] = u;
last[v] = i;
dis[v] = dis[u] + e[i].cos;
flow[v] = min(flow[u], e[i].cap);
if (!vis[v]) {
q.push(v);
vis[v] = 1;
}
}
}
}
return pre[t] != -1;
}
void MCMF() {
while (spfa()) {
ans[++k] = ans[k-1] + 1ll*flow[t] * dis[t];
int now = t;
while (now != s) {
e[last[now]].cap -= flow[t];
e[last[now] ^ 1].cap += flow[t];
now = pre[now];
}
}
}
ll ff(int i, int j) {
return (ll)(p[i].a * j * j + p[i].b * j + p[i].c);
}
int index(int m) {
return lower_bound(b.begin(), b.end(), m) - b.begin() + 1;//取地址
}
void solve() {
scanf("%d %d", &n, &m);
memset(h, -1, sizeof h);
memset(ans, 0, sizeof ans);
cnt = 0;
k = 0;
for (int i = 1; i <= n; i++) a[i].clear();
b.clear();
for (int i = 1; i <= n; i++) scanf("%lld %lld %lld", &p[i].a, &p[i].b, &p[i].c);
s = 0, t = N-2;
for (int i = 1; i <= n; i++) {
add(s, i, 1, 0);
int pos = -(p[i].b / (2 * p[i].a));//对称轴
pos = max(1, pos);//如果小于1,则从1开始往上取
pos = min(pos, m);//如果大于m,就从m开始往下取
for (int j = pos, tot = 0; j >= 1 && tot <= n; j--, tot++) a[i].push_back(j);
for (int j = pos + 1, tot = 0; j <= m && tot <= n; j++, tot++) a[i].push_back(j);
sort(a[i].begin(), a[i].end(), [&](int x, int y) { //排序
return ff(i, x) < ff(i, y);
});
a[i].erase(a[i].begin() + n, a[i].end()); //保留n个数
for (auto x : a[i]) b.push_back(x);//用于离散化处理
}
sort(b.begin(), b.end());
b.erase(unique(b.begin(), b.end()), b.end());//离散化
for (int i = 1; i <= b.size(); i++) {//所有机器向汇点连边
add(i + n, t, 1, 0);
}
for (int i = 1; i <= n; i++) {
for (auto x : a[i]) {
add(i, index(x) + n, 1, ff(i, x));//员工和机器连边
}
}
MCMF();
for (int i = 1; i <= k; i++) {
if (i != k) printf("%lld ", ans[i]);
else printf("%lld\n", ans[i]);
}
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
solve();
}
}