HDU 6070 Dirt Ratio(二分+线段树)
来源:互联网 发布:python微信公众号开发 编辑:程序博客网 时间:2024/05/29 12:30
题意:给你一个长度为n的数组,让你求size(l, r)/(r-l+1)的最小值。[l, r]是给定数组一个子序列,size(l, r)的值为这个区间的不同数的个数。(n <= 6e4)
官方题解:
二分答案mid,检验是否存在一个区间满足r−l+1size(l,r)≤mid,也就是size(l,r)+mid×l≤mid×(r+1)。
从左往右枚举每个位置作为r,当r变化为r+1时,对size的影响是一段区间加1,线段树维护区间最小值即可。时间复杂度O(nlognlogw)。
现在要找的是size(l,r)+mid*l的最小值。在维护线段树的时候,一开始先是一颗空树,然后每次加入一个数(从左到右,位置从1~n)就查询一下。
为什么这样呢?试想一下当我插入一个数的时候会对哪些区间造成影响,也就是哪些区间之前是没有当前插入的这个数的。那应该是当前插入的这个
数与上一次插入这个数的时候的位置的这段区间(记住我充计的是区间的不同数的个数),那么就对这段区间+1。然后再查询,查询的是以 i 为右端点
的最小值是多少。为什么只枚举R不枚举L呢?这里可以结合代码看一下,在线段树中当确定了R之后就会从线段树的根节点开始一直往下查找,知道R
不满足了位置,在这个过程中其实是对所有的L已经遍历了的,所以只需要枚举R就行了(挺巧妙的)。因为每添加一个数就对以当前添加的这个数的
位置为R进行一次查询,所以不仅会对所有的区间查询还对后面的没有影响(后面的数还没添加)。因为有精度,所以二分的次数有限。
代码:
#include<bits/stdc++.h>using namespace std;const int maxn = 1e5+5;int pre[maxn], a[maxn], lazy[maxn*4], n;double tree[maxn*4];void push_up(int root){ tree[root] = min(tree[root*2], tree[root*2+1]);}void push_down(int root, int l, int r){ if(lazy[root]) { int mid = (l+r)/2; tree[root*2] += lazy[root]; tree[root*2+1] += lazy[root]; lazy[root*2] += lazy[root]; lazy[root*2+1] += lazy[root]; lazy[root] = 0; }}void build(int root, int l, int r, double val){ lazy[root] = 0; if(l == r) { tree[root] = val*l; return; } int mid = (l+r)/2; build(root*2, l, mid, val); build(root*2+1, mid+1, r, val); push_up(root);}void update(int root, int l, int r, int i, int j, int val){ if(i <= l && j >= r) { lazy[root] += val; tree[root] += val; return ; } push_down(root, l, r); int mid = (l+r)/2; if(i <= mid) update(root*2, l, mid, i, j, val); if(j > mid) update(root*2+1, mid+1, r, i, j, val); push_up(root);}double query(int root, int l, int r, int i, int j){ if(i <= l && j >= r) return tree[root]; push_down(root, l, r); int mid = (l+r)/2; double ans = 1e9; if(i <= mid) ans = min(ans, query(root*2, l, mid, i, j)); if(j > mid) ans = min(ans, query(root*2+1, mid+1, r, i, j)); return ans;}int main(void){ int _; cin >> _; while(_--) { scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); double l = 0.0, r = 1.0; double ans = 1.0; for(int i = 0; i <= 20; i++) { double mid = (l+r)/2; build(1, 1, n, mid); memset(pre, 0, sizeof(pre)); bool ok = 0; for(int i = 1; i <= n; i++) { update(1, 1, n, pre[a[i]]+1, i, 1); pre[a[i]] = i; double tmp = query(1, 1, n, 1, i); if(tmp <= mid*(i+1)) { ok = 1; break; } } if(ok) r = mid, ans = mid; else l = mid; } printf("%.10f\n", ans); } return 0;}
阅读全文
1 0
- HDU 6070 Dirt Ratio 线段树 二分
- HDU 6070 Dirt Ratio [二分+线段树]
- hdu 6070 Dirt Ratio 二分,线段树
- hdu 6070 Dirt Ratio(线段树+二分)
- hdu 6070 Dirt Ratio二分 线段树
- HDU 6070 Dirt Ratio 线段树 + 二分
- 【HDU 6070 Dirt Ratio】 二分 & 线段树
- HDU 6070 Dirt Ratio 二分+线段树
- HDU 6070 Dirt Ratio(二分+线段树)
- HDU-6070 Dirt Ratio(二分+线段树+分数规划)
- hdu 6070 Dirt Ratio(二分+线段树维护区间最小值)
- HDU 6070 Dirt Ratio(二分+线段树)
- hdu 6070 Dirt Ratio(线段树+二分答案)
- HDU 6070 Dirt Ratio (线段树+二分)
- HDU 6070 Dirt Ratio(二分+线段树)
- HDU 6070 Dirt Ratio 线段树
- HDU 6070 Dirt Ratio 分数规划 二分 线段树维护区间最值
- HDU 6070 Dirt Ratio (二分+线段树, 2017 Multi-Univ Training Contest 4)
- C_结构体
- solr检索案例
- python递归报错 RuntimeError: maximum recursion depth exceeded
- mysql死锁的原因及解决办法
- 274. H-Index
- HDU 6070 Dirt Ratio(二分+线段树)
- Android Studio快捷键
- 【项目经理之修炼(11)】《初级篇》什么样的项目经理才可能成功
- charles之断点mock
- adb shell 和iperf 使用记录
- Xpath初学
- 使用不同Console命令,调试JS更便捷
- 自定义控件那些事儿 ----- 一
- unpack requires a bytes object of length 4