1. 程式人生 > 實用技巧 >2020杭電HDU多校第二場New Equipments(三分+費用流)

2020杭電HDU多校第二場New Equipments(三分+費用流)

Problem Description

Little Q's factory recently purchased m pieces of new equipment, labeled by 1,2,…,m.

There are n workers in the factory, labeled by 1,2,…,n. Each worker can be assigned to no more than one piece of equipment, and no piece of equipment can be assigned to multiple workers. If Little Q assigns the i-th worker to the j-th piece of equipment, he will need to pay ai×j^2+bi×j+ci dollars.

Now please for every k (1≤k≤n) find k pairs of workers and pieces of equipment, then assign workers to these pieces of equipment, such that the total cost for these k workers is minimized.

Input
The first line of the input contains a single integer T (1≤T≤10), the number of test cases.

For each case, the first line of the input contains two integers n and m (1≤n≤50, n≤m≤10^8), denoting the number of workers and the number of pieces of equipment.

Each of the following n lines contains three integers ai,bi and ci (1≤ai≤10, −10^8≤bi≤10^8, 0≤ci≤10^16, bi^2≤4aici), denoting a worker.

Output
For each test case, output a single line containing n integers, the k-th (1≤k≤n) of which denoting the minimum possible total cost for k pairs of workers and pieces of equipment.

Sample Input
1
3 5
2 3 10
2 -3 10
1 -1 4

Sample Output
4 15 37

emmmm,本來比賽的時候可以寫出來的。。。結果演了一波。。。a,b,c的值沒用陣列儲存下來,然後後面還用到了a,b,c,調到絕望QAQ。。。賽後一發AC。。。QAQ

首先,我們觀察,可以用三分來求出每個人在區間\([1,n]\)中的最小匹配值,然後我們向兩邊拓展,拓展n個匹配,那麼我們就可以得到每個人的最小的n個匹配。我們對其建邊,跑一波費用流就完事了,每次跑一波殘餘網路,跑n次殘餘網路就完事了。

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

typedef long long ll;
const int mac=1e5+10;
const ll inf=1e18+7;
const int INF=1e7+10;

struct edge
{
	int next, to, f;
	ll w;
}eg[mac << 1];
int head[mac], num = 1;
int pre[mac], vis[mac],flows[mac];
int s, t, maxflow, flag[mac];
ll dis[mac];

inline void add(int u, int v, int f, ll w) 
{ 
	eg[++num].to = v; eg[num].next = head[u]; eg[num].f = f; 
	eg[num].w = w; head[u] = num; 

	eg[++num].to = u; eg[num].next = head[v]; eg[num].f = 0;
	eg[num].w = -w; head[v] = num;
}
void update(int x, int flow)
{
	eg[pre[x]].f -= flow;
	eg[pre[x] ^ 1].f += flow;
	if (eg[pre[x] ^ 1].to)update(eg[pre[x] ^ 1].to, flow);
}
inline ll spfa()
{
	memset(vis, 0, sizeof(vis)); 
	queue<int>q;
	for (int i = 0; i <= t; i++) dis[i] = inf;
	flows[s] = INF; dis[s] = 0; q.push(s); vis[s] = 1;
	while (!q.empty()){
		int u = q.front(); 
		q.pop(); vis[u] = 0;
		for (int i = head[u]; i!=-1; i = eg[i].next){
			int v = eg[i].to;
			if (dis[v] > dis[u] + eg[i].w && eg[i].f){
				dis[v] = dis[u] + eg[i].w;
				pre[v] = i;
				flows[v] = min(flows[u], eg[i].f);
				if (!vis[v]){
					vis[v] = 1;
					q.push(v);
				}
			}
		}
	}
	if (dis[t] == inf) return inf;
	maxflow += flows[t];
	update(t, flows[t]); 
	return (ll)flows[t] * dis[t];
}
ll a[mac],b[mac],c[mac];

ll fff(int i,ll x)
{
	return a[i]*x*x+b[i]*x+c[i];
}

int pot[60][60];

void init()
{
	memset(head,-1,sizeof head);
	memset(flows,0,sizeof flows);
	memset(pre,0,sizeof pre);
	num=1;
}

int main(int argc, char const *argv[])
{
	int Cas;
	scanf ("%d",&Cas);
	while (Cas--){
		init();
		int n,m;
		scanf ("%d%d",&n,&m);
		for (int i=1; i<=n; i++){
			scanf ("%lld%lld%lld",&a[i],&b[i],&c[i]);
			int l=1,r=m,ans;
			while (l<r){
				int mid1=floor(1.0*(2*l+r)/3);
				int mid2=floor(1.0*(l+2*r+2)/3);
				if (fff(i,mid1)<fff(i,mid2)){
					ans=mid1;
					r=mid2-1;
				}
				else {
					ans=mid2;
					l=mid1+1;
				}
			}
			int cnt=1;
			pot[i][cnt]=ans;//三分出的當前區域的最小值點
			l=ans-1,r=ans+1;//由最優質點向兩邊拓展n個點
			while (cnt<n){
				if (fff(i,l)<fff(i,r)){
					if (l>=1 && l<=m) {
						pot[i][++cnt]=l;
						l--;
					}
					else {
						pot[i][++cnt]=r;
						r++;
					}
				}
				else {
					if (r>=1 && r<=m){
						pot[i][++cnt]=r;
						r++;
					}
					else {
						pot[i][++cnt]=l;
						l--;
					}
				}
			}
		}
		unordered_map<int,int>q;//給每個點標號
		int cnt=0;
		for (int i=1; i<=n; i++){
			for (int j=1; j<=n; j++){
				if (!q[pot[i][j]]){
				 	q[pot[i][j]]=++cnt;
				}
			}
		}
		s=0;t=cnt+n+1;
		for (int i=1; i<=n; i++){
			add(s,cnt+i,1,0);
			for (int j=1; j<=n; j++){
				add(cnt+i,q[pot[i][j]],1,fff(i,pot[i][j]));
			}
		}
		for (int i=1; i<=cnt; i++) add(i,t,1,0);
		ll ans=0;
		for (int i=1; i<=n; i++){
			ll x=spfa();//每次跑個殘餘網路
			printf("%lld%c",ans+x,i==n?'\n':' ');
			ans+=x;
		}
	}
	return 0;
}