1. 程式人生 > >【bzoj1150】[CTSC2007]數據備份Backup 模擬費用流+鏈表+堆

【bzoj1150】[CTSC2007]數據備份Backup 模擬費用流+鏈表+堆

細節 要求 限制 辦公 turn brush 相減 pre del

題目描述

你在一家 IT 公司為大型寫字樓或辦公樓(offices)的計算機數據做備份。然而數據備份的工作是枯燥乏味的,因此你想設計一個系統讓不同的辦公樓彼此之間互相備份,而你則坐在家中盡享計算機遊戲的樂趣。已知辦公樓都位於同一條街上。你決定給這些辦公樓配對(兩個一組)。每一對辦公樓可以通過在這兩個建築物之間鋪設網絡電纜使得它們可以互相備份。然而,網絡電纜的費用很高。當地電信公司僅能為你提供 K 條網絡電纜,這意味著你僅能為 K 對辦公樓(或總計2K個辦公樓)安排備份。任一個辦公樓都屬於唯一的配對組(換句話說,這 2K 個辦公樓一定是相異的)。此外,電信公司需按網絡電纜的長度(公裏數)收費。因而,你需要選擇這 K 對辦公樓使得電纜的總長度盡可能短。換句話說,你需要選擇這 K 對辦公樓,使得每一對辦公樓之間的距離之和(總距離)盡可能小。下面給出一個示例,假定你有 5 個客戶,其辦公樓都在一條街上,如下圖所示。這 5 個辦公樓分別位於距離大街起點 1km, 3km, 4km, 6km 和 12km 處。電信公司僅為你提供 K=2 條電纜。

技術分享 上例中最好的配對方案是將第 1 個和第 2 個辦公樓相連,第 3 個和第 4 個辦公樓相連。這樣可按要求使用 K=2 條電纜。第 1 條電纜的長度是 3km-1km=2km ,第 2 條電纜的長度是 6km-4km=2km。這種配對方案需要總長 4km 的網絡電纜,滿足距離之和最小的要求。

輸入

輸入的第一行包含整數n和k,其中n(2 ≤ n ≤100 000)表示辦公樓的數目,k(1≤ k≤ n/2)表示可利用的網絡電纜的數目。接下來的n行每行僅包含一個整數(0≤ s ≤1000 000 000), 表示每個辦公樓到大街起點處的距離。這些整數將按照從小到大的順序依次出現。

輸出

輸出應由一個正整數組成,給出將2K個相異的辦公樓連成k對所需的網絡電纜的最小總長度。

樣例輸入

5 2
1
3
4
6
12

樣例輸出

4


題解

模擬費用流+鏈表+堆

本題正常題解應該是“貪心+堆”,但自從某次集訓get到了模擬費用流的思想後,用在這道題裏是再恰當不過了。

先前綴相減得到相鄰兩建築物之間的距離,然後想到dp,但是MLE+TLE;考慮如果是費用流的話,該怎樣建圖?

技術分享

(邊權中第一個為容量,第二個為費用)

那麽考慮EK費用流的過程:先選擇一條最短路,加入到答案,然後將路徑上的邊容量-1,反向邊容量+1。

那麽我們模擬這個過程:

首先找到SS->S->3->2->T這條最短路,然後把它路徑上的邊容量-1,反向邊容量+1,得到下圖:

技術分享

(受到畫圖軟件的限制,只能畫成這個樣子,如果在草紙上推的話直接將原來的正向邊反過來就行)

這個時候我們會發現3->S、T->2這兩條反向邊是沒有用處的,所以直接刪掉就好。

技術分享

(再次尷尬)

這回我們發現,1->2、反向邊2->3、3->4可以合並成一條新的邊1->4,費用為2-1+2。

那麽就可以在原圖中把1->2、3->2、3->4的邊都刪了,再加上1->4的邊。

技術分享

使用鏈表實現這個過程,並用堆維護最小費用。刪除什麽的,對邊打個標記就好了。

最後不斷選出邊加到答案中,最後得到的就是最小費用了。

時間復雜度$O(n\log n)$。

註意:如果選出的邊是邊界上的邊(如1->2),那麽不能進行加邊操作,但是必須進行刪邊操作(和2相連的不能再選)

代碼細節挺多

#include <cstdio>
#include <cstring>
#include <queue>
#include <utility>
#define N 100010
using namespace std;
priority_queue<pair<int , int> > q;
int tot , d[N] , last[N << 1] , next[N << 1] , val[N << 1];
bool del[N << 1];
int main()
{
	int n , k , i , u , ans = 0;
	scanf("%d%d" , &n , &k);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &d[i]);
	for(i = 2 ; i < n ; i ++ ) last[i] = i - 1;
	for(i = 1 ; i < n - 1 ; i ++ ) next[i] = i + 1;
	for(i = 1 ; i < n ; i ++ ) val[i] = d[i + 1] - d[i] , q.push(make_pair(-val[i] , i));
	tot = n - 1;
	while(k -- )
	{
		while(del[q.top().second]) q.pop();
		u = q.top().second , q.pop() , ans += val[u] , del[u] = del[last[u]] = del[next[u]] = 1 , next[last[u]] = last[next[u]] = 0;
		if(!last[u]) last[next[next[u]]] = 0;
		else if(!next[u]) next[last[last[u]]] = 0;
		else
		{
			val[++tot] = val[last[u]] + val[next[u]] - val[u] , last[tot] = last[last[u]] , next[tot] = next[next[u]];
			if(last[tot]) next[last[tot]] = tot;
			if(next[tot]) last[next[tot]] = tot;
			q.push(make_pair(-val[tot] , tot));
		}
	}
	printf("%d\n" , ans);
	return 0;
}

【bzoj1150】[CTSC2007]數據備份Backup 模擬費用流+鏈表+堆