[NOI 2016]国王饮水记
Description
给出 \(n\) 个水杯,每个水杯装有不同高度的水 \(h_i\) ,每次可以指定任意多水杯用连通器连通后断开,问不超过 \(k\) 次操作之后 \(1\) 号水杯的最高水量。需要输出 \(q\) 位小数。(提供高精度小数库,单次计算 \(O(q)\) )
\(1\leq n\leq 8000,1\leq k\leq 10^9,1\leq h_i\leq 10^5\)
Solution
做这道题的过程中想到的几个显然的结论:
- 高度小于 \(h_1\) 的水杯不会对 \(1\) 产生影响;
- 这样我们一开始处理的时候就可以将高度小于 \(h_1\) 的去掉
- 一个水杯只会被连通一次
- 连接的顺序按高度从小到大
- 我们可以按高度从小到大排序来做
- 同一组(一起连通的水杯)一定是排好序的连续的一段区间
- 可以用反证法来证,如果不是这样选,可以通过交换的方式得到连续是更优的解
- 组与组之间没有空隙
- 如果有空隙,那么可以将高度小一点的组每一个都向更大的选一个,这样一定会更优秀
这样就可以得到一个 \(O(n^2k)\) 的转移。记 \(f_{i,j}\) 表示选了 \(i\) 个组最右端点为 \(j\) 时 \(1\) 号水杯最大的高度为 \(f_{i,j}\) ,转移为
\[f_{i,j}=\max_{0\leq k< j}\left\{\frac{f_{i-1,k}+sum_j-sum_k}{j-k+1}\right\}\]
其中 \(sum\) 是高度的前缀和。
这个式子是可以斜率优化的,并且满足决策单调性,可以做到转移 \(O(nk)\) 。这里显然 \(k=\min\{n,k\}\) 。
不过高精度库计算会有 \(O(p)\) 的复杂度。只能通过 \(70pts\) 。一个比较好的想法就是我们转移过程中还是用 \(double\) 转移,记录下转移方向。最后再算。
虽然理论复杂度似乎可行,不过这样还是过不了...
发现标算用了一个更加奇巧奇淫的性质(考场上我是绝对搞不出来的)
就是选取区间长度是单调不增的,进而可以得到长度大于 \(1\) 的选取的区间最多只有 \(14\) 个(证明的话可以参见年鉴或者题解 \(\text{PPT}\) )。
那么复杂度就是 \(O(14n+pn)\) 的了。
Code
#include <bits/stdc++.h>
using namespace std;
// ---------- decimal lib start ----------
//为了美观,这里略去了高精度小数库。
//只要将题目提供的高精度小数库粘在这里就是完整的代码了。
// ---------- decimal lib end ----------
const int N = 8000+5;
#define pdd pair<double, double>
#define fr first
#define sc second
int n, k, p, h[N], tot, h1, sum[N], head, tail, q[N], pre[15][N], lim;
double f[15][N];
pdd a[N], t;
Decimal ans;
double K(pdd a, pdd b) {return (b.sc-a.sc)/(b.fr-a.fr); }
void cal(int i, int j) {
if (i == 0 || j == 0) ans = h1;
else {cal(i-1, pre[i][j]); ans = (ans+sum[j]-sum[pre[i][j]])/(j-pre[i][j]+1); }
}
void work() {
scanf("%d%d%d", &n, &k, &p);
for (int i = 1; i <= n; i++) scanf("%d", &h[i]); h1 = h[1];
for (int i = 1; i <= n; i++) if (h[i] > h1) h[++tot] = h[i];
n = tot; sort(h+1, h+n+1); k = min(n, k);
for (int i = 1; i <= n; i++) sum[i] = sum[i-1]+h[i];
lim = min(14, k);
for (int i = 0; i <= n; i++) f[0][i] = h1;
for (int i = 0; i <= lim; i++) f[i][0] = h1;
for (int i = 1; i <= lim; i++) {
head = tail = 0; q[tail] = 0; a[0] = pdd(-1, -h1);
for (int j = 1; j <= n; j++) {
t = pdd(j, sum[j]);
while (head < tail && K(a[q[head]], t) < K(a[q[head+1]], t)) ++head;
f[i][j] = (f[i-1][q[head]]+sum[j]-sum[q[head]])/(1.*j-q[head]+1);
pre[i][j] = q[head];
a[j] = pdd(j-1, 1.*sum[j]-f[i-1][j]);
while (head < tail && K(a[q[tail-1]], a[q[tail]]) > K(a[q[tail]], a[j])) --tail;
q[++tail] = j;
}
}
int locj = n-(k-lim), loci; double maxn = 0;
for (int i = 1; i <= lim; i++) if (f[i][locj] > maxn) maxn = f[i][locj], loci = i;
cal(loci, locj);
for (int i = locj+1; i <= n; i++) ans = (ans+h[i])/2;
cout << ans.to_string(int(1.5*p)) << "\n";
}
int main() {work(); return 0; }
[NOI 2016]国王饮水记的更多相关文章
- 【BZOJ4654】【NOI2016】国王饮水记(动态规划,斜率优化)
[BZOJ4654][NOI2016]国王饮水记(动态规划,斜率优化) 题面 BZOJ 洛谷 题解 首先肯定是找性质. 明确一点,比\(h_1\)小的没有任何意义. 所以我们按照\(h\)排序,那么\ ...
- [UOJ#223][BZOJ4654][Noi2016]国王饮水记
[UOJ#223][BZOJ4654][Noi2016]国王饮水记 试题描述 跳蚤国有 n 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 1 号城市中.跳蚤国最大的问题就是饮水问题,由于首都中居住的跳 ...
- luogu P1721 [NOI2016]国王饮水记 斜率优化dp 贪心 决策单调性
LINK:国王饮水记 看起来很不可做的样子. 但实际上还是需要先考虑贪心. 当k==1的时候 只有一次操作机会.显然可以把那些比第一个位置小的都给扔掉. 然后可以得知剩下序列中的最大值一定会被选择. ...
- [Noi2016]国王饮水记
来自FallDream的博客,未经允许,请勿转载,谢谢. 跳蚤国有 n 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 1 号城市中.跳蚤国最大的问题就是饮水问题,由于首都中居住的跳蚤实在太多,跳蚤国王 ...
- BZOJ4654/UOJ223 [Noi2016]国王饮水记
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- uoj233/BZOJ4654/洛谷P1721 [Noi2016]国王饮水记 【dp + 斜率优化】
题目链接 uoj233 题解 下面不加证明地给出几个性质: 小于\(h[1]\)的城市一定是没用的 任何城市联通包含\(1\)且只和\(1\)联通一次 联通顺序从小到大最优 单个联通比多个一起联通要优 ...
- *UOJ#223. 【NOI2016】国王饮水记
$n \leq 8000$的数列,问不超过$m \leq 1e9$次操作后第一个数字最大是多少.操作:选一些数,把他们变成他们的平均值.需要保留$p \leq 3000$位小数,提供了一个小数高精度库 ...
- LOJ#2087 国王饮水记
解:这个题一脸不可做... 比1小的怎么办啊,好像没用,扔了吧. 先看部分分,n = 2简单,我会分类讨论!n = 4简单,我会搜索!n = 10,我会剪枝! k = 1怎么办,好像选的那些越大越好啊 ...
- BZOJ4654 NOI2016国王饮水记(动态规划+三分)
有很多比较显然的性质.首先每个城市(除1外)至多被连通一次,否则没有意义.其次将城市按水位从大到小排序后,用以连通的城市集合是一段前缀,并且不应存在比1城市还小的.然后如果确定了选取的城市集合,每次选 ...
随机推荐
- Linux Shell学习笔记:exit退出状态代码
inux提供$?特殊变量来保存最后一条命令执行结束的退出状态.执行完一条命令后,立即执行echo$?,可以查看最后一条命令的退出状态值. 正常的情况下,命令成功执行完成的退出状态是0,如果非0,则命令 ...
- VS2017 v15.8.0 Task ExpandPriContent failed. Illegal characters in path
昨天更新了VS到最新版本v15.8.0,但是编译UWP出现了操蛋的bug. 谷歌一下,vs社区已经有答案了. 打开.csproj文件,在节点 <PropertyGroup> 里面,加上一行 ...
- 解决SHAREJPOINT 跨域问题
目前仅支持IE7/8不支持IE11和谷歌 对于跨域情况,目前找到如果jquery是get获取方式,可以配置web.config相关属性,具体powershell命令如下: Add-PSSnapin M ...
- [R]关于R语言的绘图函数
1. 首先就是plot(x,y,...) 参数: x: 所绘图形横坐标构成的对象 y: 所绘图形纵坐标构成的对象 type: 指定所绘图形类型 pch: 指定绘制点时使用的符号 cex: 指定符号的大 ...
- 201621123018《java程序设计》第11周作业总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 1. 源代码阅读:多线程程序BounceThread 1.1 BallR ...
- 利用adb 打开手机应用程序
通过adb打开android应用,我们需要做的第一步,就是查看当前app的入口,第二部,就是通过命令启动我们的app入口 查看app的启动画面 在运行下面命令时,先启动想要通过adb打开的app #查 ...
- Linux系统软件包的管理(4)
虽然使用源码编译安装可以具有提高速度个性化的定制等优点,但对于 Linux发行商来说,则不容易管理软件包,毕竟不是每个人都会进行源码编译的,如果能够将软件预先在相同的硬体与系统上面编译好在发布的话,不 ...
- BZOJ NOIP提高组十连测第一场
今天的题目一共拿了$180$分,感觉自己还是太菜了,二三两题只能骗到部分分 1.$String\ Master$ 题目大意:有两个字符串,在允许k次失配的情况下,求最长公共子串的长度 没什么好讲,直接 ...
- jzoj3084
發現題目函數本質是: 1.將某一數x的末尾1去掉 2.不斷將這個數/2,直到遇到新的1 我們發現一個數z可以用y步到達數x,記x二進制長度為c,分2種情況討論: 1.x是奇數,則z的前c個二進制數必須 ...
- FileAttributeView出现空指针异常原因分析
问题? Java7新增了关于文件属性信息的一些新特性,通过java.nio.file.*包下面的类可以实现设置或者读取文件的元数据信息(比如最后修改时间,创建时间,文件大小,是否为目录等等).尤其 ...