传送门

首先比h1h_1h1​小的肯定没用,直接无视

然后考虑合并的顺序

①在无限制的情况下,合并多个不如一个一个合并

a<b<ca<b<ca<b<c时,a+b2+c2>a+b+c3{{a+b \over 2}+c\over 2}>{{a+b+c}\over 3}22a+b​+c​>3a+b+c​

②先合并小的比先合并大的优

整理一下,就是把>h1> h_1>h1​的从小到大排序,然后每次把当前的和序列中靠前的若干个合并

显然是个斜率优化的模型

设fk,nf_{k,n}fk,n​表示前nnn个合并kkk次的最大值

fk,n=max⁡1≤i<nfi+Sn−Sin−i+1f_{k,n}=\max_{1\leq i<n}\frac{f_i+S_n-S_i}{n-i+1}fk,n​=1≤i<nmax​n−i+1fi​+Sn​−Si​​

推一下式子发现很恶心

但注意到这个式子本身就是斜率的形式

可以看成所有(i−1,Si−fi)(i-1,S_i-f_i)(i−1,Si​−fi​)到(n,Sn)(n,S_n)(n,Sn​)的最大斜率

意识流一波,维护一个下凸壳,每次最优决策是过(n,Sn)(n,S_n)(n,Sn​)的一条与凸壳切于端点的直线,可以三分一下……

注意到hhh严格单调增,所以(n,Sn)(n,S_n)(n,Sn​)本身就组成了下凸壳

对于两个决策k1,k2k_1,k_2k1​,k2​,设iii是k2k_2k2​优于k1k_1k1​的(i,Si)(i,S_i)(i,Si​),即(k1−1,Sk1−fk1),(k2−1,Sk2−fk2),(i,Si)(k_1-1,S_{k_1}-f_{k_1}),(k_2-1,S_{k_2}-f_{k_2}),(i,S_i)(k1​−1,Sk1​​−fk1​​),(k2​−1,Sk2​​−fk2​​),(i,Si​)往上翘


如图,ki−1,i>kk2,ik_{i-1,i}>k_{k_2,i}ki−1,i​>kk2​,i​,又因为ki,i+1>ki−1,ik_{i,i+1}>k_{i-1,i}ki,i+1​>ki−1,i​,所以对于i+1i+1i+1 k2k_2k2​仍然更优

所以可以把k1k_1k1​弹掉

这样可以做到O(nkp)O(nkp)O(nkp) 这里因为最多只能取n−1n-1n−1次合并,所以n,kn,kn,k是同阶的

然后可以用低精度算dpdpdp并记录决策,最后用高精度算一次 复杂度O(nk+np)O(nk+np)O(nk+np)

// This is an empty program with decimal lib#include <cstdlib>
#include <cstring>
#include <string>
#include <cctype>
#include <cstdio>
// ---------- decimal lib start ----------const int PREC = 3500;class Decimal {public:Decimal();Decimal(const std::string &s);Decimal(const char *s);Decimal(int x);Decimal(long long x);Decimal(long double x);bool is_zero() const;// p (p > 0) is the number of digits after the decimal pointstd::string to_string(int p) const;long 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, long double x);friend Decimal operator + (long 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, long double x);friend Decimal operator - (long 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 += (long double x);Decimal & operator += (const Decimal &b);Decimal & operator -= (int x);Decimal & operator -= (long long x);Decimal & operator -= (long 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 calledfriend Decimal operator * (const Decimal &a, long double x);friend Decimal operator * (long double x, const Decimal &a);friend Decimal operator / (const Decimal &a, long double x);Decimal & operator *= (long double x);Decimal & operator /= (long 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(long 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 signwhile (*s != 0) {if (*s == '-') {is_neg = true;++s;break;} else if (*s >= 48 && *s <= 57) {break;}++s;}// read the integer partwhile (*s >= 48 && *s <= 57) {integer = integer * 10 + *s - 48;++s;}// read the decimal partif (*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;
}long double Decimal::to_double() const {long double ret = integer;long 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 >= 0if (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 <= 0if (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 >= 0if (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 <= 0if (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 >= 0if (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 <= 0if (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 >= 0if (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 <= 0if (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 = 0if (!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| - breturn -(-a + b);} else {// a - -|b|return a + -b;}
}Decimal operator + (const Decimal &a, long double x) {return a + Decimal(x);
}Decimal operator + (long double x, const Decimal &a) {return Decimal(x) + a;
}Decimal operator - (const Decimal &a, long double x) {return a - Decimal(x);
}Decimal operator - (long double x, const Decimal &a) {return Decimal(x) - a;
}Decimal & Decimal::operator += (long double x) {*this = *this + Decimal(x);return *this;
}Decimal & Decimal::operator -= (long 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 ----------
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
#include <algorithm>
#include <iostream>
#define MAXN 8005
using namespace std;
int s[MAXN],tot;
long double f[2][MAXN];
short key[MAXN][MAXN];
int q[MAXN],head,tail;
inline long double calc(int n,int i,int t){return (s[n]-s[i]+f[t^1][i])/(n-i+1);}
inline bool cmp(int i,int j,int k,int t){return (s[i]-f[t^1][i]-s[j]+f[t^1][j])*(i-k)<(s[i]-f[t^1][i]-s[k]+f[t^1][k])*(i-j);}
Decimal getans(int k,int n)
{if (!k) return s[1];return (s[n]-s[key[k][n]]+getans(k-1,key[k][n]))/(n-key[k][n]+1);
}
long double ans=0;
int pos;
int main()
{int n,k,p;n=read(),k=read(),p=read();s[tot=1]=read();for (int i=1;i<n;i++){int t=read();if (t>s[1]) s[++tot]=t;}n=tot;sort(s+1,s+n+1);if (k>=n) k=n-1;for (int i=1;i<=n;i++) s[i]+=s[i-1],f[0][i]=s[1];f[1][1]=s[1];for (int i=1,t=1;i<=k;i++,t^=1){head=1,tail=1;q[1]=1;for (int j=2;j<=n;j++){while (head<tail&&calc(j,q[head],t)<calc(j,q[head+1],t)) ++head;f[t][j]=calc(j,key[i][j]=q[head],t);while (head<tail&&cmp(j,q[tail],q[tail-1],t)) --tail;q[++tail]=j;}}cout<<getans(k,n).to_string(2*p);return 0;
}

【NOI2016】国王饮水记【贪心】【斜率优化】【决策单调性】相关推荐

  1. [loj2087][NOI2016]国王饮水记

    前言 回归OI,随便找一道清真dp题写写吧 做完发现一点都不清真 题目相关 链接 题目大意 现在有nnn个数,每次可以取若干个数,将每个数赋成平均值,限制kkk次,问第一个数最大能变成多少 数据范围 ...

  2. [jzoj 4249] 【五校联考7day1】游戏 {贪心/斜率优化}

    题目 Description WYF从小就爱乱顶,但是顶是会造成位移的.他之前水平有限,每次只能顶出k的位移,也就是从一个整点顶到另一个整点上.我们现在将之简化到数轴上,即从 一个整点可以顶到与自己相 ...

  3. LOJ#2087 国王饮水记

    解:这个题一脸不可做... 比1小的怎么办啊,好像没用,扔了吧. 先看部分分,n = 2简单,我会分类讨论!n = 4简单,我会搜索!n = 10,我会剪枝! k = 1怎么办,好像选的那些越大越好啊 ...

  4. [2020-11-23 contest]图(dfs剪枝),劫富济贫(字典树),小A的树(树形DP),游戏(贪心/斜率优化)

    文章目录 T1:图 solution code T2:劫富济贫 solution code T3:小A的树 solution code T4:游戏 solution code T1:图 [问题描述] ...

  5. 【ROI 2019 Day2】课桌【贪心】【决策单调性】【分治】

    题意:有 mmm 个班,每个班有 2n2n2n 个人,他们的身高给定.有 kkk 种双人桌,每张桌子有两个属性值 Li,RiL_i,R_iLi​,Ri​,一个身高为 hhh 的人坐第 iii 种桌子的 ...

  6. 决策单调性Ⅱ:斜率优化(1597: [Usaco2008 Mar]土地购买)

    决策单调性Ⅰ:四边形不等式: http://blog.csdn.net/jaihk662/article/details/78174717 决策单调性: 对于dp[i] = min(dp[i], dp ...

  7. 【斜率优化】【决策单调】xjb讲课

    大佬们要让蒟蒻给他们讲斜率优化 --.(色色发抖 斜率优化是什么呢,先看点没用的东西. 考虑一类动态规划,满足这样的性质: 状态数有nn个,一个状态的决策数有nn个 形似f[i]=♂i1f[j]f[i ...

  8. C++剑指offer:解题报告之DP优化学习记 (二) ——浅论DP斜率优化 (Print Article 【HDU - 3507】 )

    链接:https://share.weiyun.com/5LzbzAc 目录 前言 斜率优化前期准备 1.从状态转移方程出发 2.推理状态转移方程 对结论的进一步推导 干货!综合结论 判断斜率大小的方 ...

  9. Gym - 101471D Money for Nothing(决策单调性+分治+贪心)

    题目链接:点击查看 题目大意:在二维平面中给出 n 个点可以作为矩形左下角的点,再给出 m 个点可以作为矩形右上角的点,现在问最大可以构造出多大面积的矩形,即如何选择,可以使得 ( b[ j ] . ...

最新文章

  1. 送餐机器人---取餐完成的确认方式
  2. 【面试练习题】使用“暴力”的方法将字符串时间转化为秒数
  3. poj 1185 NYOJ 85 炮兵阵地(状态压缩dp)
  4. 形似棺材的“抗震救生床”,你会要吗?
  5. Git 回滚动任意版本
  6. linux常用文本操作命令
  7. ThinkingInJava 学习 之 0000001 一切都是对象
  8. java给图片增加水印(图片水印,文字水印)
  9. 配置华为防火墙ssh ASDM
  10. mi5splus android9,小米5s plus和华为mate9买哪个好 小米5sPlus和华为mate9区别对比评测...
  11. CTF比赛必备常用工具
  12. 考研可以跨计算机专业吗,食品安全的本科生考研,可以跨考计算机专业吗?
  13. scala spark sql 获得分组后的分位点
  14. 我是马云:新入职员工勿批判公司
  15. KMP —— 字符串分析算法
  16. 《5G时代:生活方式和商业模式的大变革》读书笔记和总结
  17. charles证书过期如何处理
  18. 主流数据库实时同步/实时ETL工具的比较
  19. hadoop上传和下载文件过程【博学谷学习记录】
  20. 什么是DFT?FT、FS、DTFT、DFS、DFT的关系

热门文章

  1. 想不到吧,这些都能用R!
  2. idea mysql 创建表_idea 根据数据库表自动创建持久化类
  3. python 装饰器实现_Python装饰器系列01 - 如何正确地实现装饰器
  4. 不同用户同时并发测压_简单聊聊吞吐量(TPS)、QPS、并发数、响应时间(RT)概念...
  5. linux挂载4t硬盘用不了,centos7挂载新加4T硬盘到/home目录
  6. c语言哈密顿路径算法,用于检查给定图中是否存在哈密顿循环或路径的C ++程序...
  7. request-promise 获取返回头信息_http返回的状态码 大全
  8. 此 sqltransaction 已完成;它再也无法使用_手把手教你如何修眉毛,学会再也不用花钱去美容院了...
  9. leetcode98. 验证二叉搜索树
  10. ciclop读音,购机必备,15种 3D扫描 设备 优缺点汇总