题面

传送门

此题的题意不是很清晰,要注意的一点是在区间[L,R]中,默认题目编号最后一次出现的时候是AC的

比如1 2 1 2 3 ,在区间[1,4]中,第3次提交时AC第1题,第4次提交时AC第2题,故比例为2/4=0.5

所以此问题可以转化为

给定一个序列,定义区间[l,r]的值为cnt(l,r)r−l+1" role="presentation" style="position: relative;">cnt(l,r)r−l+1cnt(l,r)r−l+1,(cnt(l,r)为区间中不同元素的个数,求值最大的区间,输出其最大值

分析

1.二分答案

很明显此题用直接枚举区间的方法会超时,必须使用二分来降低时间复杂度

如果二分区间端点,则答案不存在单调性,无法二分

因此二分答案,显然答案可取的范围是[0,1)

设二分中点为mid,由题意cnt(l,r)r−l+1≤mid" role="presentation" style="position: relative;">cnt(l,r)r−l+1≤midcnt(l,r)r−l+1≤mid ([l,r]为所有区间中值最小的区间)

两边同乘化简r-l+1,移项,整理得cnt(l,r)+mid×l≤mid×(r+1)" role="presentation" style="position: relative;">cnt(l,r)+mid×l≤mid×(r+1)cnt(l,r)+mid×l≤mid×(r+1)

2.求不等式左边的值,判断mid是否可取到

对于每次二分,枚举r从1~n,对于每一个r,我们再求右端点为r的所有区间的所有区间的值中的最小值

我们维护一棵线段树,线段数的每个叶子节点l表示左端点为l,右端点为r(当前的枚举值)时的值,再统计区间[1,r]最小值

这样我们就求出了右端点为r的所有区间的值中的最小值

由于r从1~n,线段树中的值也在不断变化,我们怎么更新线段树中的值呢?

每次r+1,需要更新[r,r]这个区间,区间加mid×r" role="presentation" style="position: relative;">mid×rmid×r,

再更新[x,r]各区间的cnt值,设a上一次出现的位置为last[a],则区间[last[a]+1,r]都需要+1,因为区间内出现了一个新的数a,而对于last[a]之前的位置,由于a已经出现过,cnt值不受影响

时间复杂度分析:

二分时间复杂度为O(log21ϵ)" role="presentation" style="position: relative;">O(log21ϵ)O(log21ϵ),其中 ϵ" role="presentation" style="position: relative;">ϵϵ为精度,在此题中设ϵ=10−5" role="presentation" style="position: relative;">ϵ=10−5ϵ=10−5即可

线段树的建树时间复杂度为O(nlog2n)" role="presentation" style="position: relative;">O(nlog2n)O(nlog2n),n次插入与查询时间复杂度为O(nlog2n)" role="presentation" style="position: relative;">O(nlog2n)O(nlog2n)

所以总的时间复杂度分析为O(nlog2n×log21ϵ)" role="presentation" style="position: relative;">O(nlog2n×log21ϵ)O(nlog2n×log21ϵ)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 60005
#define eps 1e-5
#define INF 0x7fffffff
using namespace std;
int a[maxn];
int pos[maxn],last[maxn];
struct node{
int l;
int r;
double mark;
double v;
int len(){
return r-l+1;
}
}tree[maxn<<2];
void push_up(int pos){
tree[pos].v=min(tree[pos<<1].v,tree[pos<<1|1].v);
}
void build(int l,int r,int pos){
tree[pos].l=l;
tree[pos].r=r;
tree[pos].v=0;
tree[pos].mark=0;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,pos<<1);
build(mid+1,r,pos<<1|1);
}
void push_down(int pos){
if(tree[pos].mark!=0){
tree[pos<<1].v+=tree[pos].mark;
tree[pos<<1|1].v+=tree[pos].mark;
tree[pos<<1].mark+=tree[pos].mark;
tree[pos<<1|1].mark+=tree[pos].mark;
tree[pos].mark=0;
}
}
void update(int L,int R,double v,int pos){
int l=tree[pos].l,r=tree[pos].r;
if(L<=l&&R>=r){
tree[pos].v+=v;
tree[pos].mark+=v;
return;
}
push_down(pos);
int mid=(l+r)>>1;
if(L<=mid) update(L,R,v,pos<<1);
if(R>mid) update(L,R,v,pos<<1|1);
push_up(pos);
}
double query(int L,int R,int pos){
int l=tree[pos].l,r=tree[pos].r;
if(L<=l&&R>=r){
return tree[pos].v;
}
push_down(pos);
int mid=(l+r)>>1;
double ans=INF;
if(L<=mid) ans=min(ans,query(L,R,pos<<1));
if(R>mid) ans=min(ans,query(L,R,pos<<1|1));
return ans;
}
int t,n;
int main(){
scanf("%d",&t);
while(t--){
memset(last,0,sizeof(last));
memset(pos,0,sizeof(pos));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
last[i]=pos[a[i]];
pos[a[i]]=i;
}
double l=0,r=1;
double ans=INF;
while(fabs(l-r)>eps){
build(1,n,1);
double mid=(l+r)/2;
bool is_min=false;
for(int i=1;i<=n;i++){
update(i,i,mid*i,1);
update(last[i]+1,i,1,1);
double t=query(1,i,1);
if(mid*(i+1)>=t){
is_min=true;
break;
}
}
if(is_min){
r=mid;
ans=min(ans,mid);
}
else l=mid;
}
printf("%lf\n",ans);
}
}

HDU 6070题解(二分+线段树)的更多相关文章

  1. hdu 6070 Dirt Ratio 线段树+二分

    Dirt Ratio Time Limit: 18000/9000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)Spe ...

  2. K-th occurrence HDU - 6704 (后缀数组+二分线段树+主席树)

    大意: 给定串s, q个询问(l,r,k), 求子串s[l,r]的第kk次出现位置. 这是一篇很好的题解: https://blog.csdn.net/sdauguanweihong/article/ ...

  3. hdu6070 Dirt Ratio 二分+线段树

    /** 题目:hdu6070 Dirt Ratio 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6070 题意:给定n个数,求1.0*x/y最小是多少.x ...

  4. hdu 5700区间交(线段树)

    区间交 Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submiss ...

  5. Educational Codeforces Round 61 D 二分 + 线段树

    https://codeforces.com/contest/1132/problem/D 二分 + 线段树(弃用结构体型线段树) 题意 有n台电脑,只有一个充电器,每台电脑一开始有a[i]电量,每秒 ...

  6. Snacks HDU 5692 dfs序列+线段树

    Snacks HDU 5692 dfs序列+线段树 题意 百度科技园内有n个零食机,零食机之间通过n−1条路相互连通.每个零食机都有一个值v,表示为小度熊提供零食的价值. 由于零食被频繁的消耗和补充, ...

  7. HDU4614 Vases and Flowers 二分+线段树

    分析:感觉一看就是二分+线段树,没啥好想的,唯一注意,当开始摆花时,注意和最多能放的比大小 #include<iostream> #include<cmath> #includ ...

  8. J - Joseph and Tests Gym - 102020J (二分+线段树)

    题目链接:https://cn.vjudge.net/contest/283920#problem/J 题目大意:首先给你n个门的高度,然后q次询问,每一次询问包括两种操作,第一种操作是将当前的门的高 ...

  9. 【BZOJ-3110】K大数查询 整体二分 + 线段树

    3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6265  Solved: 2060[Submit][Sta ...

随机推荐

  1. 如何删除Github上的仓库

    1.首先,进入自己的github账户页面,点击头像选择下面的Your repositorys,点击进入 2.进入以后选中自己要删除的仓库,点击进去该仓库界面 3.找到Settings按钮,点击进入 4 ...

  2. Azure IoT 技术研究系列4

    上两篇博文中,我们介绍了将设备注册到Azure IoT Hub,设备到云.云到设备之间的通信: Azure IoT 技术研究系列2-设备注册到Azure IoT Hub Azure IoT 技术研究系 ...

  3. Python----公开课

    # 构造函数- 类在实例化的时候,执行一些基础性的初始化的工作- 使用特殊的名称和写法- 在实例化的时候自动执行- 是在实例化的时候第一个被执行的函数- ----------------------- ...

  4. [洛谷P4107] HEOI2015 兔子与樱花

    问题描述 很久很久之前,森林里住着一群兔子.有一天,兔子们突然决定要去看樱花.兔子们所在森林里的樱花树很特殊.樱花树由n个树枝分叉点组成,编号从0到n-1,这n个分叉点由n-1个树枝连接,我们可以把它 ...

  5. [BZOJ3714] Kuglarz

    问题描述 魔术师的桌子上有n个杯子排成一行,编号为1,2,-,n,其中某些杯子底下藏有一个小球,如果你准确地猜出是哪些杯子,你就可以获得奖品.花费c_ij元,魔术师就会告诉你杯子i,i+1,-,j底下 ...

  6. nyoj 952 : 最大四边形 (计算几何)

    题目链接 任意四边形均可看作是两个三角形拼接得到的(即使是凹四边形),故 可以O(n^2)枚举所有的线段,然后对每条线段O(n)枚举线段端点外的其他点,用来更新以此线段构成的三角形的有向面积的最大值m ...

  7. Quick BI的SQL传参建模可以用在什么场景

    Quick B的SQL传参建模功能提供基于SQL的数据加工处理能力,减轻了IT支撑人员的工作量.在即席查询SQL中,我们用物理字段显示别名来表示参数的占位符,配置完占位符后,就可以在查询控件中进行参数 ...

  8. 判断Java对象存活的算法、垃圾回收算法

    判断Java对象存活的算法 一.引用计数算法 给对象添加一个引用计数器,每当一个地方引用它的时候就将计数器加1,当引用失效的时候就将计数器减1,任何时刻计数器为0的对象都不可再被使用.这种算法虽然简单 ...

  9. 循环结构for语句-求和思想

    循环结构for语句的练习-求和思想:需求1:求出1到10之间的数据和 public static void main(String[] args) { int sum = 0; for(int i = ...

  10. 版本基线自动化之windows

    1.背景: 目前项目维护周期过程中,制作调试版本和对外发布版本次数比较频繁,流程过于繁琐和随意,且打包制作人成为瓶颈,为了规范版本基线流程和实现全员自动化参与,拟定版本基线自动化方案. 2.目标: 版 ...