[Noi2016]国王饮水记
来自FallDream的博客,未经允许,请勿转载,谢谢。
跳蚤国有 n 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 1 号城市中。跳蚤国最大的问题就是饮水问题,由于首都中居住的跳蚤实在太多,跳蚤国王又体恤地将分配给他的水也给跳蚤国居民饮用,这导致跳蚤国王也经常喝不上水。于是,跳蚤国在每个城市都修建了一个圆柱形水箱,这些水箱完全相同且足够高。一个雨天后,第 i 个城市收集到了高度为 hi 的水。由于地理和天气因素的影响,任何两个不同城市收集到的水高度互不相同。跳蚤国王也请来蚂蚁工匠帮忙,建立了一个庞大的地下连通系统。跳蚤国王每次使用地下连通系统时,可以指定任意多的城市,将这些城市的水箱用地下连通系统连接起来足够长的时间之后,再将地下连通系统关闭。由连通器原理,这些城市的水箱中的水在这次操作后会到达同一高度,并且这一高度等于指定的各水箱高度的平均值。由于地下连通系统的复杂性,跳蚤国王至多只能使用 k 次地下连通系统。跳蚤国王请你告诉他,首都 1 号城市水箱中的水位最高能有多高?
n<=8000 hi<=10^5
至少保留p位小数 p<=3000
提供了一个高精度小数类
首先考虑只保留大于1号城市的高度,并且发现从小到大合并比较优秀 所以按照从小到大排序 求出前缀和Hi
然后每次都计算太麻烦了,所以用一个trick,先用long double计算出转移路径再计算答案 (题解里面一套分数类啥的可以直接做)
列出转移方程$f[k][i]=max(\frac{f[k-1][j]+si-sj}{i-j+1})$ 考虑维护(j-1,sj-f[k-1][j])的下凸壳,发现它满足决策单调性 于是复杂度降低至$O(n^{2}+np)$
但是根据题解里面一系列的结论,每次取的长度不同且递减,长度大于1的最多只有14个,所以先dp出14层,然后剩下的直接两两合并就行了。复杂度O(14n+np)
// This is a test program with decimal lib #include <cstdlib> #include <cstring> #include <string> // ---------- decimal lib start ---------- const int PREC = 2100; 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 = 500; 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 ---------- #include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define MN 8000 #define ld long double inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int n , m , k , K , p , h[MN + 5] , Pre , top , From[17][MN + 5]; ld f[17][MN + 5]; struct P { int x; ld y; P(int _x = 0,ld _y = 0): x(_x) , y(_y) {} friend ld Calc(P a , P b) { return (b.y - a.y) / (b.x - a.x); } }; struct MyQueue { P q[MN + 5];int top , tail; void Clear(){ q[top = tail = 1] = P( -1 , -Pre);} void ins(P t) { while(top > tail && Calc(q[top-1] , q[top]) > Calc(q[top] , t)) --top; q[ ++top ] = t; } int Query(P t) { while(top > tail && Calc(q[tail] , t) < Calc(q[tail + 1] , t)) ++tail; return q[tail].x + 1; } }Q; Decimal Ans; void Dfs(int t , int x) { if(!t) return; Dfs( t - 1 , From[t][x] ); Ans = ( Ans + h[x] - h[From[t][x]] ) / (x - From[t][x] + 1); } int main() { m = read(); K = read(); p = read(); Pre = read(); for(int i = 1 , j;i < m ;++i) if((j = read()) > Pre) h[++n] = j; sort(h+1 , h + n + 1); K = min(K , n); k = min(K , 14); for(int i = 1 ;i <= n ;++i) h[i] += h[i-1] , f[0][i] = Pre;h[0] = -Pre; for(int j = 1;j <= k ;++j) { Q.Clear(); for(int i = 1 ;i <= n ; ++i) { From[j][i] = Q.Query(P(i , h[i])); f[j][i] = (f[j - 1][From[j][i]] + h[i] - h[ From[j][i] ]) / (i - From[j][i] + 1); Q.ins(P(i - 1 , h[i] - f[j - 1][i])); } } Ans = Decimal ( Pre ); h[0] = 0; Dfs ( k , n - (K - k) ); for(int i = n - (K - k) + 1;i <= n ; ++i) Ans = (Ans + h[i] - h[i-1]) / 2; cout << Ans.to_string(p + 2); return 0; }