[UOJ#223][BZOJ4654][Noi2016]国王饮水记
[UOJ#223][BZOJ4654][Noi2016]国王饮水记
试题描述
输入
输出
输入示例
输出示例
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)\n", 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;
}
[UOJ#223][BZOJ4654][Noi2016]国王饮水记的更多相关文章
- BZOJ4654 NOI2016国王饮水记(动态规划+三分)
有很多比较显然的性质.首先每个城市(除1外)至多被连通一次,否则没有意义.其次将城市按水位从大到小排序后,用以连通的城市集合是一段前缀,并且不应存在比1城市还小的.然后如果确定了选取的城市集合,每次选 ...
- luogu P1721 [NOI2016]国王饮水记 斜率优化dp 贪心 决策单调性
LINK:国王饮水记 看起来很不可做的样子. 但实际上还是需要先考虑贪心. 当k==1的时候 只有一次操作机会.显然可以把那些比第一个位置小的都给扔掉. 然后可以得知剩下序列中的最大值一定会被选择. ...
- BZOJ4654/UOJ223 [Noi2016]国王饮水记
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- uoj233/BZOJ4654/洛谷P1721 [Noi2016]国王饮水记 【dp + 斜率优化】
题目链接 uoj233 题解 下面不加证明地给出几个性质: 小于\(h[1]\)的城市一定是没用的 任何城市联通包含\(1\)且只和\(1\)联通一次 联通顺序从小到大最优 单个联通比多个一起联通要优 ...
- [Noi2016]国王饮水记
来自FallDream的博客,未经允许,请勿转载,谢谢. 跳蚤国有 n 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 1 号城市中.跳蚤国最大的问题就是饮水问题,由于首都中居住的跳蚤实在太多,跳蚤国王 ...
- P1721 [NOI2016] 国王饮水记 题解
蒟蒻的第一篇黑题题解,求过. 题目链接 题意描述 这道题用简洁的话来说,就是: 给你 \(n\) 个数字,你可以让取其中任意若干个数字,每次操作,都会使所有取的数字变为取的数字的平均数,并且你最多只能 ...
- 【BZOJ4654】【NOI2016】国王饮水记(动态规划,斜率优化)
[BZOJ4654][NOI2016]国王饮水记(动态规划,斜率优化) 题面 BZOJ 洛谷 题解 首先肯定是找性质. 明确一点,比\(h_1\)小的没有任何意义. 所以我们按照\(h\)排序,那么\ ...
- *UOJ#223. 【NOI2016】国王饮水记
$n \leq 8000$的数列,问不超过$m \leq 1e9$次操作后第一个数字最大是多少.操作:选一些数,把他们变成他们的平均值.需要保留$p \leq 3000$位小数,提供了一个小数高精度库 ...
- [NOI 2016]国王饮水记
Description 题库链接 给出 \(n\) 个水杯,每个水杯装有不同高度的水 \(h_i\) ,每次可以指定任意多水杯用连通器连通后断开,问不超过 \(k\) 次操作之后 \(1\) 号水杯的 ...
随机推荐
- ssh框架出现Java.lang.NoSuchMethodError: antlr.collections.AST.getLine()I错误
原因:因为Struts自带的antlr-2.7.2.jar,比Hibernate自带的antlr-2.7.7.jar的版本要低,存在jar包冲突现象,因此要删除前一个低版本的. 由于myeclipse ...
- uvm_reg_sequence——寄存器模型(六)
寄存器模型 uvm_reg_sequence是UVM自带所有register sequence 的基类. 该类包含model, adapter, reg_seqr(uvm_sequencer). 感觉 ...
- 【数据分析 R语言实战】学习笔记 第八章 方差分析与R实现
方差分析泛应用于商业.经济.医学.农业等诸多领域的数量分析研究中.例如商业广告宣传方面,广告效果可能会受广告式.地区规模.播放时段.播放频率等多个因素的影响,通过方差分析研究众多因素中,哪些是主要的以 ...
- Javafinal方法
class Animal{ public final void eat(){ System.out.println("吃"); } } class ...
- 爬虫_python3_抓取猫眼电影top100
使用urllib,request,和正则表达式,多线程进行秒抓,以及异常处理结果: import urllib,re,json from multiprocessing import Pool#多进程 ...
- jQuery绑定动态元素的点击事件无效
之前就一直受这个问题的困扰,在写ajax加载数据的时候发现,后面追加进来的demo节点元素,失去了之前的点击事件.为什么点击事件失效,我们该怎么去解决呢?那么,我们通过下面的示例简单说明. 示例如下: ...
- mac系统下android studio创建手机模拟器
打开android studio,点击右上角的模拟器图标,打开“Android Virtual Device Manager” 窗口,如下图 点击“Create Virtual Device”,在 ...
- flask模板语言
由于Django的模板引擎和Flask中的Jinja2模板引擎有很多一样的地方,所以我将一样的地方总结到了独立的文章中 https://www.cnblogs.com/kuxingseng95/art ...
- iOS Crash
常见原因及解决方法: 1. 访问数组类对象越界或插入了空对象NSMutableArray/NSMutableDictionary/NSMutableSet 等类下标越界,或者 insert 了一个 n ...
- WORD与DWORD
在看C/C++的书或者试题时,有时会见到利用word或dword定义的变量,第一次看到的时候并不知其是什么,更不用说word或dword占几个字节了.幸好在VC安装文件夹下有相关的定义.如C:\Pro ...