BZOJ4654/UOJ223 [Noi2016]國王飲水記


本文版權歸ljh2000和博客園共有,歡迎轉載,但須保留此聲明,並給出原文鏈接,謝謝合作。

 

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
轉載請注明出處,侵權必究,保留最終解釋權!

 

Description

跳蚤國有 n 個城市,偉大的跳蚤國王居住在跳蚤國首都中,即 1 號城市中。跳蚤國最大的問題就是飲水問題,由
於首都中居住的跳蚤實在太多,跳蚤國王又體恤地將分配給他的水也給跳蚤國居民飲用,這導致跳蚤國王也經常喝
不上水。於是,跳蚤國在每個城市都修建了一個圓柱形水箱,這些水箱完全相同且足夠高。一個雨天后,第 i 個
城市收集到了高度為 hi 的水。由於地理和天氣因素的影響,任何兩個不同城市收集到的水高度互不相同。跳蚤國
王也請來螞蟻工匠幫忙,建立了一個龐大的地下連通系統。跳蚤國王每次使用地下連通系統時,可以指定任意多的
城市,將這些城市的水箱用地下連通系統連接起來足夠長的時間之后,再將地下連通系統關閉。由連通器原理,這
些城市的水箱中的水在這次操作后會到達同一高度,並且這一高度等於指定的各水箱高度的平均值。由於地下連通
系統的復雜性,跳蚤國王至多只能使用 k 次地下連通系統。跳蚤國王請你告訴他,首都 1 號城市水箱中的水位最
高能有多高?
 

Input

輸入的第一行包含 3 個正整數 n,k,p分別表示跳蚤國中城市的數量,跳蚤國王能使用地下連通系統的最多次數,
以及你輸出的答案要求的精度。p 的含義將在輸出格式中解釋。接下來一行包含 n 個正整數,描述城市的水箱在
雨后的水位。其中第 i 個 正整數 hi 表示第 i 個城市的水箱的水位。保證 hi 互不相同,1≤hi≤10^5
對於所有數據,滿足3≤p≤3000,1≤n≤8000,1≤k≤10^9
 

Output

僅一行一個實數,表示 1 號城市的水箱中的最高水位。這個實數只可以包含非負整數部分、小數點和小數部分。
其中非負整數部分為必需部分,不加正負號。若有小數部分,則非負整數部分與小數部分之間以一個小數點隔開。
若無小數部分,則不加小數點。你輸出的實數在小數點后不能超過 2p 位,建議保留至少 p 位。數據保證參考答
案與真實答案的絕對誤差小於 10^-2p。你的輸出被判定為正確當且僅當你的輸出與參考答案的絕對誤差小於 10^-
p
 

Sample Input

3 1 3
1 4 3

Sample Output

2.666667
 

Explanation
由於至多使用一次地下連通系統,有以下 5 種方案:
1. 不使用地下連通系統:此時 1 號城市的水箱水位為 1。
2. 使用一次連通系統,連通 1、2 號:此時 11 號城市的水箱水位為 5/2。
3. 使用一次連通系統,連通 1、3 號:此時 1 號城市的水箱水位為 2/2。
4. 使用一次連通系統,連通 2、3號:此時 1 號城市的水箱水位為 1。
5. 使用一次連通系統,連通 1、2、3號:此時 11 號城市的水箱水位為 8/3。
 
 
 
 
正解:決策單調性+斜率優化+DP
解題報告:
  Picks學長出的NOI2016神題
 
 
   首先列出所有要用到的定理:
   定理一:所有水量小於等於1號城市水量的城市都不對答案產生貢獻。
 
      證明:這個應該是顯然的吧...
 
 
 
  (下面所有定理均略去“在最優方案中”辣) 
  定理二: 除一號城市外,每個城市最多被連通一次。 
  定理三: 每一次連通都一定和1號城市連通
 
   證明:首先可以發現兩個定理等價,嘿嘿嘿。
      定理二推導定理三: 一次連接如果未和1號城市連接,則之后這些城市就沒有意義了。可以刪去這次操作。
      定理三推導定理二: 一個城市若和1號城市連通一次,此時其水量等於1號城市,不會出現第二次。
      
      考慮使用數學歸納法證明:

        當只有一次操作時顯然。不妨假設對於最后m個操作,他們均成立,接下來只要考慮倒數第m+ 1個操作。

        若該操作包含了1號城市,則由等價性可直接得出結論。

        若該操作不包含1號城市,注意到在這次操作之后,與本次操作相關聯的水杯都變為了同一高度

        即我們可以任意交換他們在之后的編號。

 

        假設存在一些城市在之后也用到了。

        由於后m個操作都滿足這兩個定理,我們可以發現:

        通過交換編號,可以使得這些城市的水量自出現順序從后向前由大到小變化。

 

        且此時刪去第m + 1次操作所得答案更優。(就是沒有用辣)      

 
 
  定理四:當k → ∞時,最優方案排序后由小到大將比1號城市大的城市與1號城市依次相接。
     
    證明:注意到有無窮多次操作時,任意一個操作都可以拆成無數次某城市與1號城市的連接。
       此時只需要考慮所有選擇1號城市和某城市的操作即可。
       顯然一個城市連接完之后就廢了,且任意兩個城市一定是水量小的先連接。那么只要按順序連接一下就好了。
       (關於這里說水量小的先連接,可以感性認識一下呀...
        如果和大的先連接,那么小的相較之下最后也會被拔高一點點,就會產生“浪費”)
      
    
 
  定理五:每次操作選擇的城市的最小水量一定大於前一次操作所選擇的城市的最大水量
 
    證明:這個應該反證法很好證呀。只要存在不滿足的情況的話,我們交換之后,答案肯定更優。
       可以通過列交換前后的高度變化的不等式來證明這樣是肯定更優的。
 
 
  定理六:每次操作選擇的城市均是選擇排序后連續的一段城市(選擇一個區間)。
  
    證明:我會反證法!嘿嘿嘿
       考慮如果去掉水量最小的城市,再選擇一個兩段的間隔處(或者叫做斷點處)的一座城市。  
         這樣答案必然比之前更優。
 
 
  定理七:任意兩次相鄰操作選擇的區間之間不存在城市(列緊密)。
 
    證明:將靠左側的區間右移一位,顯然更優。
 
 
  
 
  定理五、六、七一出,題目模型就可以轉化辣,是不是有一種熟悉的感覺,然后就很資瓷了啊!
    
  我們很快可以得到一個區間$DP$的常用轉移式:
  令$f_{i,j}$表示到第$i$個城市,用了$j$次連通器后$1$號城市的最高水位,則
    
      ${f_{i,j}=max_{k<i}(\frac{f_{k,j-1}+S_i-S_k}{i-k+1}) }$
      其中,$S_{i}$表示水量前綴和
     
    因為這個高精度小數庫是$O(p)$的,所以這個復雜度是:$O(n^2Kp)$
 
  然而這個式子顯然可以斜率優化,那么我們再把這個式子變變形:
  
        ${f_{i,j}=max_{k<i}(\frac{S_i-(S_k-f_{k,j-1})}{i-(k-1)}) }$
      
    就可以維護一個凸包,然后每次在上一層的凸包上三分即可。
    復雜度:$O(nkplogn)$
 
  然后似乎有決策單調性呀!打個表,發現確實是這樣的...
  之后就是常規的單調性斜率DP的優化,不贅述辣...證的話其實還是可以證明的!只是很難打公式的...窩這么懶,還是算了吧...
  
    然后復雜度就變成了:$O(nkp)$
 
  
  就如Picks的講題PPT中說的,我們似乎有個條件沒用:所有水量高度互不相同!
  呀!我們又可以得到兩個新的結論:
  
  1、每一次操作的區間長度一定不比上一次操作的區間長度長!
  2、在所有水量高度互不相同的情況下,長度大於1的區間僅有$O(log{\frac{nh}{H}})$個,其中$H=min(h_{i}-h_{i-1})$
 
  第一個結論可以很快證明出來,因為反證法同樣可以證明呀...
  假設存在,然后列一列交換前后的答案的計算式,會發現不等關系顯然成立。
    
  第二個結論,窩好不容易搞懂了,看還是看得懂得...然而睡了一覺起來又忘了...看來以后搞懂之后必須馬上寫下來。
  但是也並不好推...
  那怎么辦呢?
  打表!找規律!
  會發現其實很多層轉移之后,每次的操作區間很快就會變成長度為1的區間。
  大概發現最多14層,之后就不會再有大於1的區間出現。這種結論肯定是在考場上打表和猜猜猜+對拍!這比證明要快得多...
  那么我們DP14層之后就可以直接計算答案了。
  最后的總復雜度為$O(nplog(nh))$
  
  ps:這道題如果在知道結論之后就變得異常好做了...然而如果要完整證明所有的定理...GG...    
    事實證明:打表大法好!找規律大法妙!
 
 
   補充(一些細節的說明):
    顯然我不能每次計算答案,因為高精度小數庫的復雜度同樣很高,
    我需要把每次的決策點,和從哪里轉移過來的記錄下來,最后再來跑一遍就可以了。
    注意要把高精度小數庫的位數改成3000!
    考慮最多有14個長度大於1的區間,那么我剩下的操作都是只選一個。
        所以我們就需要枚舉必須只選1個的位置之前,一共選了多少次(選的多不一定優),
        根據我記錄下來的轉移位置,遞歸計算即可。
    

 

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;

// ---------- decimal lib start ----------
const int PREC = 3000;//!!!
class Decimal {
	public:
		Decimal();
		Decimal(const std::string &s);
		Decimal(const char *s);
		Decimal(int x);
		Decimal(long long x);
		Decimal(double x);
		
		bool is_zero() const;
		
		// p (p > 0) is the number of digits after the decimal point
		std::string to_string(int p) const;
		double to_double() const;
		
		friend Decimal operator + (const Decimal &a, const Decimal &b);
		friend Decimal operator + (const Decimal &a, int x);
		friend Decimal operator + (int x, const Decimal &a);
		friend Decimal operator + (const Decimal &a, long long x);
		friend Decimal operator + (long long x, const Decimal &a);
		friend Decimal operator + (const Decimal &a, double x);
		friend Decimal operator + (double x, const Decimal &a);
		
		friend Decimal operator - (const Decimal &a, const Decimal &b);
		friend Decimal operator - (const Decimal &a, int x);
		friend Decimal operator - (int x, const Decimal &a);
		friend Decimal operator - (const Decimal &a, long long x);
		friend Decimal operator - (long long x, const Decimal &a);
		friend Decimal operator - (const Decimal &a, double x);
		friend Decimal operator - (double x, const Decimal &a);
		
		friend Decimal operator * (const Decimal &a, int x);
		friend Decimal operator * (int x, const Decimal &a);
		
		friend Decimal operator / (const Decimal &a, int x);
		
		friend bool operator < (const Decimal &a, const Decimal &b);
		friend bool operator > (const Decimal &a, const Decimal &b);
		friend bool operator <= (const Decimal &a, const Decimal &b);
		friend bool operator >= (const Decimal &a, const Decimal &b);
		friend bool operator == (const Decimal &a, const Decimal &b);
		friend bool operator != (const Decimal &a, const Decimal &b);
		
		Decimal & operator += (int x);
		Decimal & operator += (long long x);
		Decimal & operator += (double x);
		Decimal & operator += (const Decimal &b);
		
		Decimal & operator -= (int x);
		Decimal & operator -= (long long x);
		Decimal & operator -= (double x);
		Decimal & operator -= (const Decimal &b);
		
		Decimal & operator *= (int x);
		
		Decimal & operator /= (int x);
		
		friend Decimal operator - (const Decimal &a);
		
		// These can't be called
		friend Decimal operator * (const Decimal &a, double x);
		friend Decimal operator * (double x, const Decimal &a);
		friend Decimal operator / (const Decimal &a, double x);
		Decimal & operator *= (double x);
		Decimal & operator /= (double x);
		
	private:
		static const int len = PREC / 9 + 1;
		static const int mo = 1000000000;
		
		static void append_to_string(std::string &s, long long x);
		
		bool is_neg;
		long long integer;
		int data[len];
		
		void init_zero();
		void init(const char *s);
};

Decimal::Decimal() {
	this->init_zero();
}

Decimal::Decimal(const char *s) {
	this->init(s);
}

Decimal::Decimal(const std::string &s) {
	this->init(s.c_str());
}

Decimal::Decimal(int x) {
	this->init_zero();
	
	if (x < 0) {
		is_neg = true;
		x = -x;
	}
	
	integer = x;
}

Decimal::Decimal(long long x) {
	this->init_zero();
	
	if (x < 0) {
		is_neg = true;
		x = -x;
	}
	
	integer = x;
}

Decimal::Decimal(double x) {
	this->init_zero();
	
	if (x < 0) {
		is_neg = true;
		x = -x;
	}
	
	integer = (long long)x;
	x -= integer;
	
	for (int i = 0; i < len; i++) {
		x *= mo;
		if (x < 0) x = 0;
		data[i] = (int)x;
		x -= data[i];
	}
}

void Decimal::init_zero() {
	is_neg = false;
	integer = 0;
	memset(data, 0, len * sizeof(int));
}

bool Decimal::is_zero() const {
	if (integer) return false;
	for (int i = 0; i < len; i++) {
		if (data[i]) return false;
	}
	return true;
}

void Decimal::init(const char *s) {
	this->init_zero();
	
	is_neg = false;
	integer = 0;
	
	// find the first digit or the negative sign
	while (*s != 0) {
		if (*s == '-') {
			is_neg = true;
			++s;
			break;
		} else if (*s >= 48 && *s <= 57) {
			break;
		}
		++s;
	}
	
	// read the integer part
	while (*s >= 48 && *s <= 57) {
		integer = integer * 10 + *s - 48;
		++s;
	}
	
	// read the decimal part
	if (*s == '.') {
		int pos = 0;
		int x = mo / 10;
		
		++s;
		while (pos < len && *s >= 48 && *s <= 57) {
			data[pos] += (*s - 48) * x;
			++s;
			x /= 10;
			if (x == 0) {
				++pos;
				x = mo / 10;
			}
		}
	}
}

void Decimal::append_to_string(std::string &s, long long x) {
	if (x == 0) {
		s.append(1, 48);
		return;
	}
	
	char _[30];
	int cnt = 0;
	while (x) {
		_[cnt++] = x % 10;
		x /= 10;
	}
	while (cnt--) {
		s.append(1, _[cnt] + 48);
	}
}

std::string Decimal::to_string(int p) const {
	std::string ret;
	
	if (is_neg && !this->is_zero()) {
		ret = "-";
	}
	
	append_to_string(ret, this->integer);
	
	ret.append(1, '.');
	
	for (int i = 0; i < len; i++) {
		// append data[i] as "%09d"
		int x = mo / 10;
		int tmp = data[i];
		while (x) {
			ret.append(1, 48 + tmp / x);
			tmp %= x;
			x /= 10;
			if (--p == 0) {
				break;
			}
		}
		if (p == 0) break;
	}
	
	if (p > 0) {
		ret.append(p, '0');
	}
	
	return ret;
}

double Decimal::to_double() const {
	double ret = integer;
	
	double k = 1.0;
	for (int i = 0; i < len; i++) {
		k /= mo;
		ret += k * data[i];
	}
	
	if (is_neg) {
		ret = -ret;
	}
	
	return ret;
}

bool operator < (const Decimal &a, const Decimal &b) {
	if (a.is_neg != b.is_neg) {
		return a.is_neg && (!a.is_zero() || !b.is_zero());
	} else if (!a.is_neg) {
		// a, b >= 0
		if (a.integer != b.integer) {
			return a.integer < b.integer;
		}
		for (int i = 0; i < Decimal::len; i++) {
			if (a.data[i] != b.data[i]) {
				return a.data[i] < b.data[i];
			}
		}
		return false;
	} else {
		// a, b <= 0
		if (a.integer != b.integer) {
			return a.integer > b.integer;
		}
		for (int i = 0; i < Decimal::len; i++) {
			if (a.data[i] != b.data[i]) {
				return a.data[i] > b.data[i];
			}
		}
		return false;
	}
}

bool operator > (const Decimal &a, const Decimal &b) {
	if (a.is_neg != b.is_neg) {
		return !a.is_neg && (!a.is_zero() || !b.is_zero());
	} else if (!a.is_neg) {
		// a, b >= 0
		if (a.integer != b.integer) {
			return a.integer > b.integer;
		}
		for (int i = 0; i < Decimal::len; i++) {
			if (a.data[i] != b.data[i]) {
				return a.data[i] > b.data[i];
			}
		}
		return false;
	} else {
		// a, b <= 0
		if (a.integer != b.integer) {
			return a.integer < b.integer;
		}
		for (int i = 0; i < Decimal::len; i++) {
			if (a.data[i] != b.data[i]) {
				return a.data[i] < b.data[i];
			}
		}
		return false;
	}
}

bool operator <= (const Decimal &a, const Decimal &b) {
	if (a.is_neg != b.is_neg) {
		return a.is_neg || (a.is_zero() && b.is_zero());
	} else if (!a.is_neg) {
		// a, b >= 0
		if (a.integer != b.integer) {
			return a.integer < b.integer;
		}
		for (int i = 0; i < Decimal::len; i++) {
			if (a.data[i] != b.data[i]) {
				return a.data[i] < b.data[i];
			}
		}
		return true;
	} else {
		// a, b <= 0
		if (a.integer != b.integer) {
			return a.integer > b.integer;
		}
		for (int i = 0; i < Decimal::len; i++) {
			if (a.data[i] != b.data[i]) {
				return a.data[i] > b.data[i];
			}
		}
		return true;
	}
}

bool operator >= (const Decimal &a, const Decimal &b) {
	if (a.is_neg != b.is_neg) {
		return !a.is_neg || (a.is_zero() && b.is_zero());
	} else if (!a.is_neg) {
		// a, b >= 0
		if (a.integer != b.integer) {
			return a.integer > b.integer;
		}
		for (int i = 0; i < Decimal::len; i++) {
			if (a.data[i] != b.data[i]) {
				return a.data[i] > b.data[i];
			}
		}
		return true;
	} else {
		// a, b <= 0
		if (a.integer != b.integer) {
			return a.integer < b.integer;
		}
		for (int i = 0; i < Decimal::len; i++) {
			if (a.data[i] != b.data[i]) {
				return a.data[i] < b.data[i];
			}
		}
		return true;
	}
}

bool operator == (const Decimal &a, const Decimal &b) {
	if (a.is_zero() && b.is_zero()) return true;
	if (a.is_neg != b.is_neg) return false;
	if (a.integer != b.integer) return false;
	for (int i = 0; i < Decimal::len; i++) {
		if (a.data[i] != b.data[i]) return false;
	}
	return true;
}

bool operator != (const Decimal &a, const Decimal &b) {
	return !(a == b);
}

Decimal & Decimal::operator += (long long x) {
	if (!is_neg) {
		if (integer + x >= 0) {
			integer += x;
		} else {
			bool last = false;
			for (int i = len - 1; i >= 0; i--) {
				if (last || data[i]) {
					data[i] = mo - data[i] - last;
					last = true;
				} else {
					last = false;
				}
			}
			integer = -x - integer - last;
			is_neg = true;
		}
	} else {
		if (integer - x >= 0) {
			integer -= x;
		} else {
			bool last = false;
			for (int i = len - 1; i >= 0; i--) {
				if (last || data[i]) {
					data[i] = mo - data[i] - last;
					last = true;
				} else {
					last = false;
				}
			}
			integer = x - integer - last;
			is_neg = false;
		}
	}
	return *this;
}

Decimal & Decimal::operator += (int x) {
	return *this += (long long)x;
}

Decimal & Decimal::operator -= (int x) {
	return *this += (long long)-x;
}

Decimal & Decimal::operator -= (long long x) {
	return *this += -x;
}

Decimal & Decimal::operator /= (int x) {
	if (x < 0) {
		is_neg ^= 1;
		x = -x;
	}
	
	int last = integer % x;
	integer /= x;
	
	for (int i = 0; i < len; i++) {
		long long tmp = 1LL * last * mo + data[i];
		data[i] = tmp / x;
		last = tmp - 1LL * data[i] * x;
	}
	
	if (is_neg && integer == 0) {
		int i;
		for (i = 0; i < len; i++) {
			if (data[i] != 0) {
				break;
			}
		}
		if (i == len) {
			is_neg = false;
		}
	}
	
	return *this;
}

Decimal & Decimal::operator *= (int x) {
	if (x < 0) {
		is_neg ^= 1;
		x = -x;
	} else if (x == 0) {
		init_zero();
		return *this;
	}
	
	int last = 0;
	for (int i = len - 1; i >= 0; i--) {
		long long tmp = 1LL * data[i] * x + last;
		last = tmp / mo;
		data[i] = tmp - 1LL * last * mo;
	}
	integer = integer * x + last;
	
	return *this;
}

Decimal operator - (const Decimal &a) {
	Decimal ret = a;
	// -0 = 0
	if (!ret.is_neg && ret.integer == 0) {
		int i;
		for (i = 0; i < Decimal::len; i++) {
			if (ret.data[i] != 0) break;
		}
		if (i < Decimal::len) {
			ret.is_neg = true;
		}
	} else {
		ret.is_neg ^= 1;
	}
	return ret;
}

Decimal operator + (const Decimal &a, int x) {
	Decimal ret = a;
	return ret += x;
}

Decimal operator + (int x, const Decimal &a) {
	Decimal ret = a;
	return ret += x;
}

Decimal operator + (const Decimal &a, long long x) {
	Decimal ret = a;
	return ret += x;
}

Decimal operator + (long long x, const Decimal &a) {
	Decimal ret = a;
	return ret += x;
}

Decimal operator - (const Decimal &a, int x) {
	Decimal ret = a;
	return ret -= x;
}

Decimal operator - (int x, const Decimal &a) {
	return -(a - x);
}

Decimal operator - (const Decimal &a, long long x) {
	Decimal ret = a;
	return ret -= x;
}

Decimal operator - (long long x, const Decimal &a) {
	return -(a - x);
}

Decimal operator * (const Decimal &a, int x) {
	Decimal ret = a;
	return ret *= x;
}

Decimal operator * (int x, const Decimal &a) {
	Decimal ret = a;
	return ret *= x;
}

Decimal operator / (const Decimal &a, int x) {
	Decimal ret = a;
	return ret /= x;
}

Decimal operator + (const Decimal &a, const Decimal &b) {
	if (a.is_neg == b.is_neg) {
		Decimal ret = a;
		bool last = false;
		for (int i = Decimal::len - 1; i >= 0; i--) {
			ret.data[i] += b.data[i] + last;
			if (ret.data[i] >= Decimal::mo) {
				ret.data[i] -= Decimal::mo;
				last = true;
			} else {
				last = false;
			}
		}
		ret.integer += b.integer + last;
		return ret;
	} else if (!a.is_neg) {
		// a - |b|
		return a - -b;
	} else {
		// b - |a|
		return b - -a;
	}
}

Decimal operator - (const Decimal &a, const Decimal &b) {
	if (!a.is_neg && !b.is_neg) {
		if (a >= b) {
			Decimal ret = a;
			bool last = false;
			for (int i = Decimal::len - 1; i >= 0; i--) {
				ret.data[i] -= b.data[i] + last;
				if (ret.data[i] < 0) {
					ret.data[i] += Decimal::mo;
					last = true;
				} else {
					last = false;
				}
			}
			ret.integer -= b.integer + last;
			return ret;
		} else {
			Decimal ret = b;
			bool last = false;
			for (int i = Decimal::len - 1; i >= 0; i--) {
				ret.data[i] -= a.data[i] + last;
				if (ret.data[i] < 0) {
					ret.data[i] += Decimal::mo;
					last = true;
				} else {
					last = false;
				}
			}
			ret.integer -= a.integer + last;
			ret.is_neg = true;
			return ret;
		}
	} else if (a.is_neg && b.is_neg) {
		// a - b = (-b) - (-a)
		return -b - -a;
	} else if (a.is_neg) {
		// -|a| - b
		return -(-a + b);
	} else {
		// a - -|b|
		return a + -b;
	}
}

Decimal operator + (const Decimal &a, double x) {
	return a + Decimal(x);
}

Decimal operator + (double x, const Decimal &a) {
	return Decimal(x) + a;
}

Decimal operator - (const Decimal &a, double x) {
	return a - Decimal(x);
}

Decimal operator - (double x, const Decimal &a) {
	return Decimal(x) - a;
}

Decimal & Decimal::operator += (double x) {
	*this = *this + Decimal(x);
	return *this;
}

Decimal & Decimal::operator -= (double x) {
	*this = *this - Decimal(x);
	return *this;
}

Decimal & Decimal::operator += (const Decimal &b) {
	*this = *this + b;
	return *this;
}

Decimal & Decimal::operator -= (const Decimal &b) {
	*this = *this - b;
	return *this;
}

// ---------- decimal lib end ----------

const int MAXN = 8011;
Decimal ans;
int n,k,p,h[MAXN],dui[MAXN],head,tail,from[MAXN][20],s[MAXN];
double f[MAXN][20];

struct Point{ 
	double x,y;
}ljh,b[MAXN];

inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline double K(Point q,Point qq){
	return (qq.y-q.y)/(qq.x-q.x);
}

inline Decimal calc(int i,int j){
	if(j==0) return h[1];
	return ( calc(from[i][j],j-1) + s[i]-s[from[i][j]] ) / (i-from[i][j]+1);
}

inline void work(){
	n=getint(); k=getint(); p=getint(); h[1]=getint();
	for(int i=2;i<=n;i++) { h[i]=getint(); if(h[i]<=h[1]) i--,n--; }
	sort(h+1,h+n+1); for(int i=1;i<=n;i++) s[i]=s[i-1]+h[i];
	k=min(k,n); for(int i=1;i<=n;i++) f[i][0]=h[1];//不操作的話就是1本身
	int lim;
	if(k>14) lim=14; else lim=k;
	for(int j=1;j<=lim;j++) {//最多lim個長度超過1的區間
		head=tail=0; dui[tail]=1;
		b[1].x=0; b[1].y=s[1]-f[1][j-1];
		for(int i=2;i<=n;i++) {
			ljh.x=i; ljh.y=s[i];
			while(head<tail && K(b[dui[head]],ljh)<K(b[dui[head+1]],ljh)) head++;
			b[i].x=i-1; b[i].y=s[i]-f[i][j-1]; from[i][j]=dui[head];
			f[i][j]=s[i]-s[dui[head]]+f[dui[head]][j-1]; f[i][j]/=(double)(i-dui[head]+1);
			while(head<tail && K(b[dui[tail-1]],b[dui[tail]])>K(b[dui[tail]],b[i])) tail--;
			dui[++tail]=i;
		}
	}

	//要選lim個長度大於等於1的區間
	//而如果k>14則必須要選若干長度只有1的區間,否則我每次都可以選長度大於1的區間,也就是k(lim)次

	int u=n-(k-lim);//最后k-lim個必定是只選1個
	//所以u就是長度大於等於1操作區間的結束位置,必然也是最后一個決策點

	//枚舉從u往前的所有區間中操作多少次
	double suan=0; int star;
	for(int j=0;j<=lim;j++) if(f[u][j]>suan) suan=f[u][j],star=j;
	ans=calc(u,star);
	for(int i=u+1;i<=n;i++) ans=(ans+h[i])/2;
	cout<<ans.to_string(p*2);
}

int main()
{
    work();
    return 0;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM