1. 程式人生 > >【堆模擬費用流增廣】UOJ455 [UER #8] 雪災與外賣

【堆模擬費用流增廣】UOJ455 [UER #8] 雪災與外賣

【題目】
原題地址
一條直線上有 n n 個送餐員和 m m 個餐廳。每個送餐員都要去餐廳取菜,花費為距離+菜的價值。每個餐廳有提供菜數量限制,求最小花費。 n

, m 1 0 5 n,m\leq 10^5 ,其他數字
1 0 9 \leq 10^9

【解題思路】
題解看這
設送餐員為 a a ,餐廳為 b

b ,座標為 x x
首先我們可以觀察到一個十分 naive \text{naive} 的性質: 1 a i < a j n , 1 b k < b l m , x a i x b k + x a j x b l x a j x b k + x a i x b l \forall 1\leq a_i<a_j\leq n,1\leq b_k<b_l\leq m,|x_{a_i}-x_{b_k}|+|x_{a_j}-x_{b_l}|\leq |x_{a_j}-x_{b_k}|+|x_{a_i}-x_{b_l}|
於是我們按順序 DP \text{DP} ,狀態為前 i i 個送餐員匹配到前 j j 個餐廳的最小花費,轉移時列舉第 j j 個餐廳匹配多少個送餐員,用單調佇列優化可以做到 O ( n m ) O(nm)

考慮一個更顯然的問題,這個就是一個二分圖匹配問題,我們建圖跑費用流就可以了。
然而這樣做並不優秀,於是我們考慮套路,用堆或線段樹來模擬費用流的增廣,這裡顯然是用堆。

我們考慮把餐廳和送餐員放在一起按照座標排序,從前往後考慮,當考慮到一名送餐員 i i 時,我們先讓它與前面的一個空的餐廳 j j 匹配。費用為 x i x j x_i-x_j ,那麼我們就是要找一個最小的 y j -y_j 。(如果沒有空的餐廳我們可以視為在 -\infty 處有無窮個餐館)。

匹配完之後,我們可能進行調整,把這名送餐員改為匹配後面的餐廳。那麼我們就可以撤銷這次操作,然後把這名送餐員當做沒有使用過,那麼往堆中丟入 ( x i x j ) x i -(x_i-x_j)-x_i ,即減去之前這組匹配的貢獻,然後尋找新匹配。 掃到餐廳時也類似,先可以匹配一名送餐員,然後可以撤銷這組匹配,尋找新匹配。

對所有餐廳和送餐員分別建一個堆來維護這個操作。注意我們既可以對送餐員進行反悔,也可以對餐廳進行反悔,即每新增一組匹配,我們往兩個堆中分別加入反悔操作。

設每個餐廳能提供 c i c_i 的菜,複雜度是O ( ( n + c i ) log ( n + c i ) ) ((n+\sum c_i)\log (n+\sum c_i))
這個就是一個純模擬費用流了。

考慮這個過程中我們維護了兩個堆,一個維護待匹配的送餐員,一個維護待匹配的餐廳。之前複雜度不對的原因是送餐員堆的複雜度沒有保證,因為一個餐廳匹配了一名送餐員之後,這名送餐員可能反悔,所以這名送餐員又會重新丟入送餐員堆中。

但注意到對於送餐員 x , y x,y 和餐廳 a , b a,b 來說,若 x < y < a < b x<y<a<b ,那麼當送餐員 x x y y 在匹配了餐館 a a 時,丟到堆裡的元素都是 x a w a -x_a-w_a ,也就是說每次餐館匹配了若干送餐員後,往送餐員堆裡面丟的東西都是等權的,因此只需要丟一次就行了。

複雜度就是 O ( n log n ) O(n\log n) 的了。

【參考程式碼】

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

typedef long long ll;
const int N=2e5+10,INF=0x3f3f3f3f;
int n,m,cnt;
ll ans,tot;

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

struct node
{
	int x,w,c;
	node(int _x=0,int _w=0,int _c=0):x(_x),w(_w),c(_c){}
	bool operator <(const node&a)const{return x<a.x;}
}a[N];

struct heap
{
	ll v;int t;
	heap(ll _v=0,int _t=0):v(_v),t(_t){}
	bool operator <(const heap&a)const{return v==a.v?t<a.t:v>a.v;}
};
priority_queue<heap>q1,q2;

int main()
{
#ifndef ONLINE_JUDGE
	freopen("UOJ455.in","r",stdin);
	freopen("UOJ455.out","w",stdout);
#endif
	n=read();m=read();cnt=n;
	for(int i=1;i<=n;++i) a[i].x=read();
	for(int i=1;i<=m;++i)
	{
		int x=read(),w=read(),c=min(read(),n);tot+=c;
		if(c) a[++cnt]=node(x,w,c);
	}
	if(tot<n){puts("-1");return 0;}
	a[++cnt]=node(-INF,INF,N);
	sort(a+1,a+cnt+1);
	for(int i=1;i<=cnt;++i)
	{