[UOJ#223][BZOJ4654][Noi2016]国王饮水记
试题描述
输入
输出
输入示例
3 1 3 1 4 3
输出示例
2.666667
数据规模及约定
见“输入”
题解
这题居然被我卡过了 233333333
这题 91 分还是比较有意义的。
首先发现一个性质(以下所说的数组 H 均不包含城市 1 的高度以及比城市 1 矮的高度),若将数组 H 从小到大排序,会发现最优策略一定考虑最后面若干个数,并且 k 次取平均操作均是和一段连续区间取平均。
那么一个 dp 的模型就已经出来了:f(j, i) 表示进行了 j 次取平均操作,前 i 个数都被考虑过的最大能达到的平均值。
转移就是枚举这一次取平均操作是哪个区间:f(j, i) = max{ ( S[i] - S[k] + f(j-1, k) ) / ( i - k + 1 ) | 0 ≤ k < i },整理一下发现 f(j, i) = ( S[i] - ( S[k] - f(j-1, k) ) ) / ( i - ( k - 1 ) ),也就是说我们要最大化一个斜率,那么显然就是对于点集 { ( x - 1, S[x] - f(x-1, k) ) } 维护下凸壳。
进一步发现,每次转移的决策是单调增的,因为我们已经将数组 H 从小到大排序,那么点集 { ( i, S[i] ) } 构成了一个下凸壳,所以每次过 ( i, S[i] ) 的直线只会更陡,并且(画个图就知道)在维护的凸包上的切点一直在往后移。
于是初步分析我们得到 O(nkp) 的算法。
看到题解,我了解到当 k > 14 时,只需要算到 j = 14 即可,后面的转移都是长度为 1 的区间,贪心取即可。这样试了一下有 90 分。。。
后来我尝试卡常,发现我被骗了,其实只需要算到 j = 8(也许更少,现在(2017-6-30) AC 的代码只算到了 j = 8,欢迎 hack),然后题面中时刻需要保留 6/5·p 位小数也是骗人的,我只保留了 3050 位小数就过掉最后一个点了。。。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <string> // ---------- decimal lib start ---------- const int PREC = 3050; 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 ---------- int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 8010 int n, K, P, A[maxn], S[maxn]; struct Vec { int x; Decimal y; Vec() {} Vec(int _, Decimal __): x(_), y(__) {} Vec operator - (const Vec& t) const { return Vec(x - t.x, y - t.y); } Decimal operator ^ (const Vec& t) const { return x * t.y - y * t.x; } } ps[maxn]; Decimal f[2][maxn]; int q[maxn], hd, tl; void solve() { Decimal ans = A[1]; for(int i = 1; i <= n; i++) f[0][i] = A[1]; bool cur = 1; for(int j = 1; j <= std::min(K, 8); j++, cur ^= 1) { hd = 1; tl = 0; for(int i = j; i <= n; i++) { ps[i] = Vec(i - 1, S[i] - f[cur^1][i]); while(hd < tl && (ps[i] - ps[q[tl]] ^ ps[i] - ps[q[tl-1]]) >= 0) tl--; q[++tl] = i; Vec p(i, S[i]); while(hd < tl && (p - ps[q[hd]] ^ p - ps[q[hd+1]]) >= 0) hd++; p = p - ps[q[hd]]; f[cur][i] = p.y / p.x; // if(i - q[hd] != 1) printf("f(%d, %d) --> f(%d, %d) ", j - 1, q[hd], j, i); } } if(K > 8) { ans = f[cur^1][n-K+8]; for(int i = n - K + 9; i <= n; i++) ans = (ans + A[i]) / 2; } else ans = f[cur^1][n]; std::cout << ans.to_string(P << 1) << std::endl; return ; } int main() { n = read(); K = read(); P = read(); for(int i = 1; i <= n; i++) A[i] = read(); int t = n; n = 1; for(int i = 2; i <= t; i++) if(A[i] > A[1]) A[++n] = A[i]; K = std::min(K, n - 1); std::sort(A + 2, A + n + 1); for(int i = 1; i <= n; i++) S[i] = S[i-1] + A[i]; solve(); return 0; }