BZOJ4654/UOJ223 [Noi2016]国王饮水记
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
Description
Input
Output
Sample Input
1 4 3
Sample Output
由于至多使用一次地下连通系统,有以下 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。
定理三推导定理二: 一个城市若和1号城市连通一次,此时其水量等于1号城市,不会出现第二次。
当只有一次操作时显然。不妨假设对于最后m个操作,他们均成立,接下来只要考虑倒数第m+ 1个操作。
若该操作包含了1号城市,则由等价性可直接得出结论。
若该操作不包含1号城市,注意到在这次操作之后,与本次操作相关联的水杯都变为了同一高度,
即我们可以任意交换他们在之后的编号。
假设存在一些城市在之后也用到了。
由于后m个操作都满足这两个定理,我们可以发现:
通过交换编号,可以使得这些城市的水量自出现顺序从后向前而由大到小变化。
且此时删去第m + 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;
}
BZOJ4654/UOJ223 [Noi2016]国王饮水记的更多相关文章
- [UOJ#223][BZOJ4654][Noi2016]国王饮水记
[UOJ#223][BZOJ4654][Noi2016]国王饮水记 试题描述 跳蚤国有 n 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 1 号城市中.跳蚤国最大的问题就是饮水问题,由于首都中居住的跳 ...
- luogu P1721 [NOI2016]国王饮水记 斜率优化dp 贪心 决策单调性
LINK:国王饮水记 看起来很不可做的样子. 但实际上还是需要先考虑贪心. 当k==1的时候 只有一次操作机会.显然可以把那些比第一个位置小的都给扔掉. 然后可以得知剩下序列中的最大值一定会被选择. ...
- uoj233/BZOJ4654/洛谷P1721 [Noi2016]国王饮水记 【dp + 斜率优化】
题目链接 uoj233 题解 下面不加证明地给出几个性质: 小于\(h[1]\)的城市一定是没用的 任何城市联通包含\(1\)且只和\(1\)联通一次 联通顺序从小到大最优 单个联通比多个一起联通要优 ...
- BZOJ4654 NOI2016国王饮水记(动态规划+三分)
有很多比较显然的性质.首先每个城市(除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\) 号水杯的 ...
随机推荐
- Codeforces Round #324 (Div. 2) (快速判断素数模板)
蛋疼的比赛,当天忘了做了,做的模拟,太久没怎么做题了,然后C题这么简单的思路却一直卡到死,期间看了下D然后随便猜了下,暴力了下就过了. A.找一个能被t整除的n位数,那么除了<=10以外,其他都 ...
- 【BZOJ4974】字符串大师 KMP
[BZOJ4974]字符串大师 Description 一个串T是S的循环节,当且仅当存在正整数k,使得S是T^k(即T重复k次)的前缀,比如abcd是abcdabcdab的循环节.给定一个长度为n的 ...
- SharePoint服务器端对象模型 之 使用CAML进行数据查询
(一)概述 在SharePoint的开发应用中,查询是非常常用的一种手段,根据某些筛选.排序条件,获得某个列表或者某一些列表中相应的列表条目的集合. 除去列表上的查询之外,在SharePoint中还大 ...
- Kubectl工具常用命令
创建namesapce kubectl create namespace {name} 注意:name只能为小写字母.数字和-的组合,且开头结尾为字母,一般格式为my-name 123-abc等. 创 ...
- 【转】NPIV - 连接虚拟机与存储的桥梁
转自:http://blog.csdn.net/jewes/article/details/7705895 解决什么问题 我们知道在存储区域网络(SAN:storage area network),主 ...
- Linux学习拾遗
一.安装iso文件 首先建立一个目录作为挂载点:# mkdir /mnt/iso 获得root权限然后使用下面的参数挂载ISO映像文件:# mount -t iso9660 /path/image.i ...
- 兼容ie的background-size: cover;
.bg{ background: url() no-repeat; background-size:cover; filter: progid:DXImageTransform.Microsoft.A ...
- 阿里云RDS上的一些概念性记录
刚接触RDS,只能对RDS上的一些特性做一些笔记,方便记忆,以下为RDS上的内容摘录,取自官方文档 1 数据备份 可使用命令行或图形界面进行逻辑数据备份.仅限通过 RDS 管理控制台 或 OPEN A ...
- argparse 模块 在终端执行脚本文件
1.案例 #1.案例: import argparse #首先导入模块 parser = argparse.ArgumentParser() #创建一个解析对象 parser.add_argument ...
- 1、hive安装详细步骤
一.环境准备 hadoop-2.7.2 java 1.7.0 apache-hive-1.2.1 mysql Hive配置文件介绍 •hive-site.xml hive的配置文件 •hiv ...