题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6070

题目大意:给定一个序列a,对于任何一个区间 [l,r],它的“Dirt Ratio”值为区间内不同元素的个数除以区间长度。现在要你求出序列a中哪个区间的“Dirt Ratio”值最小,输出最小值。

题目思路:由于是要找最小值,我们考虑用二分来找答案。假设当前二分的答案的值为 x ,如果这个序列满足这个答案的话,必然存在一个区间的 sum[i,j] / len[i,j] <= x,(sum[i,j] 表示区间 [i,j] 内不同元素的个数,len[i,j]为区间 [i,j] 的长度),则该式子可以化为

sum[i,j] - len[i,j] * x <= 0。

对于sum[i,j] 我们可以考虑用线段树来维护,用pre[i] 表示序列a中上一个值与a[i]相同的位置,然后再按顺序往线段树内加入a[i] ,每次对线段树的区间 [pre[i] + 1, i] 进行更新+1,这样加到第 i 个时,对于满足 j < i 的sum[j] 来说就表示着区间[j,i] 内不同元素的数的个数。

接下来就只用考虑 x 的影响,每次插入a[i]时,对于线段树区间 [1,i] 更新 -x,这样当更新到第 i 个元素时,线段树内的第 j 位的元素正好被减去了(i - j + 1)个x,正好是 j 到 i 的区间长度。这样当插入到第 i 个数,线段树内的第 j 位就代表着区间 [j,i] 的“Dirt Ratio”值,线段树内再维护一下区间最小值,这样顺序插入之后就可以枚举完所有区间了。注意一下二分的次数,如果太多次了就会TLE,由于精度只需要1e-4,所以二分15~20次就够了。

具体实现看代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
const int MX = 60000 + 5;int n, _;
int a[MX], pre[MX], last[MX];
double sum[MX << 2], lz[MX << 2];
void push_up(int rt) {sum[rt] = min(sum[rt << 1], sum[rt << 1 | 1]);
}
void push_down(int rt) {if (lz[rt]) {lz[rt << 1] += lz[rt];lz[rt << 1 | 1] += lz[rt];sum[rt << 1] += lz[rt];sum[rt << 1 | 1] += lz[rt];lz[rt] = 0;}
}
void build(int l, int r, int rt) {sum[rt] = lz[rt] = 0;if (l == r) return;int m = (l + r) >> 1;build(lson); build(rson);push_up(rt);
}
void update(int L, int R, double d, int l, int r, int rt) {if (L <= l && r <= R) {sum[rt] += d;lz[rt] += d;return;}push_down(rt);int m = (l + r) >> 1;if (L <= m) update(L, R, d, lson);if (R > m) update(L, R, d, rson);push_up(rt);
}
double query(int L, int R, int l, int r, int rt) {if (L <= l && r <= R) return sum[rt];push_down(rt);double res = 1e9;int m = (l + r) >> 1;if (L <= m) res = min(res, query(L, R, lson));if (R > m) res = min(res, query(L, R, rson));return res;
}
bool check(double x) {build(1, n, 1);for (int i = 1; i <= n; i++) {update(pre[i] + 1, i, 1, 1, n, 1);update(1, i, -x, 1, n, 1);if (query(1, i, 1, n, 1) <= 0) return 1;}return 0;
}int main() {for (scanf("%d", &_); _; _--) {scanf("%d", &n);for (int i = 0; i <= n; i++) pre[i] = last[i] = 0;for (int i = 1; i <= n; i++) {scanf("%d", &a[i]);pre[i] = last[a[i]];last[a[i]] = i;}double l = 0, r = 1;for (int i = 0; i < 15; i++) {double mid = (l + r) / 2;if (check(mid)) r = mid;else l = mid;}printf("%.5f\n", l);}return 0;
}

HDU - 6070 Dirt Ratio (二分 + 线段树)相关推荐

  1. HDU 6070 Dirt Ratio(线段树、二分)

    http://acm.hdu.edu.cn/showproblem.php?pid=6070 题解 首先不难看出错误率是单调的,那么我们可以直接二分答案x,某个区间的错误率=区间数的种类cnt/区间长 ...

  2. HDU 6070 Dirt Ratio

    暑假多校赛的一道题,印象深刻,今天终于补了,现在感觉也不是特别难,题意是很经典的那种问题,就是给你一个数列,问一个区间不同的个数比区间的长度的值,在这个数列里的最小值.之前搜过区间不同数的个数的查询问 ...

  3. 线段树分裂与合并 ----- P2824 [HEOI2016/TJOI2016]排序 [线段树分裂合并 OR 01序列排序+二分线段树]

    题目链接 题目大意: 对一个序列,每次按照升序或者降序排序序列某一段,问你最后的序列是什么? 解法1:二分+线段树 首先我们知道对一个01序列进行排序是很快的!我们只要知道里面有多少个1和多少个0,那 ...

  4. HDU 1166 敌兵布阵(线段树:点更新,区间求和)

    HDU 1166 敌兵布阵(线段树:点更新,区间求和) http://acm.hdu.edu.cn/showproblem.php?pid=1166 题意: 给你n个整数,然后给你多条命令,每条命令如 ...

  5. HDU 3016 Man Down (线段树+dp)

    HDU 3016 Man Down (线段树+dp) Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Ja ...

  6. 2019CCPC网络赛 1002 HDU 6703(权值线段树)

    2019CCPC网络赛 1002 HDU 6703(权值线段树) 思路:用权值线段树存题目给的数据后,2操作就是求权值线段树中大于等于k的部分中,靠近左端点的第一个大于r的值(这个求出来的只是原序列中 ...

  7. HDU - 4614 Vases and Flowers 线段树+二分

    题目链接 思路:线段树维护区间和,当k=1时,询问二分询问[x-(x~n-1)]找到最小位置,复杂度n*logn*logn卡过 #include<stdio.h> #include< ...

  8. 【HDU - 5875】Function(线段树,区间第一个小于某个数的数 或 RMQ二分)

    题干: The shorter, the simpler. With this problem, you should be convinced of this truth.        You a ...

  9. HDU 6089 Rikka with Terrorist (线段树)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6089 题解 这波强行维护搞得我很懵逼... 扫描线,只考虑每个点能走到左上方(不包括正上方,但包括正左 ...

最新文章

  1. 手把手教你写网络爬虫(4)Scrapy入门
  2. T4模板:MVC中用T4模板快速生成代码
  3. 草稿 0242 ktv第一个页面
  4. 上位机通过串口获取单片机数据
  5. Java实验9 T5. 给当前的类文件添加行号拷贝到txt文件
  6. 安卓http服务器源码,Android搭建HTTP和WebSocket的服务器端
  7. Android应用程序键盘(Keyboard)消息处理机制分析(7)
  8. percentile函数mysql_SQL使用窗口函数计算百分位数
  9. Rhino4.0软件安装教程
  10. linux sub指令,数据处理指令之:SUB减操作指令
  11. 如何在windows开启UAC(用户账号控制)的情况下优雅的管理程序的权限申请方案(一)
  12. 向量代数,直线,平面
  13. AB-PLC之间通讯
  14. hadoop3访问hdfs web控制页面遇到的各种问题总结
  15. 如何使用VLOOKUP在Google表格中查找数据
  16. phpspreadsheet 中文文档(三) 计算引擎
  17. 多页面实现-个人中心
  18. 字符串中空格相关操作
  19. 2006-2020年全国31省人口老龄化水平
  20. python 取整法(进一取值)

热门文章

  1. 小白学习cartopy画地图的第六天
  2. 大衣哥在《火火的情怀》后,和孟文豪张成军推出《新时代的农民》
  3. 如何压缩图片jpg大小?怎么缩小jpg大小kb?
  4. 内存指令不能written
  5. Superset系列8- 制作饼图
  6. 整数转成RGB颜色值
  7. System Generator从入门到放弃(一)-安装与使用
  8. word页脚显示不全
  9. 读取 STM32F10x 内部基准电压。
  10. SpringBoot之RMI的简单使用