题面

传送门

此题的题意不是很清晰,要注意的一点是在区间[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. docker安装MySQL5.7示例!!坑

    docker  pull  mysql 一.错误的启动 [root@localhost  ~]#  docker  run  ‐‐name  mysql01  ‐d  mysql 42f0981990 ...

  2. windows 安装php

    php各个版本下载地址:https://www.apachelounge.com/viewtopic.php?t=6359 https://museum.php.net/ https://www.fu ...

  3. 为何使用Shell脚本

    为何使用Shell脚本 分类: linux shell脚本学习2012-09-12 17:18 78人阅读 评论(0) 收藏 举报 shell脚本任务工作         s h e l l 脚本在处 ...

  4. 【NOIP2012模拟8.20】Memory

    题目 话说某一天,CD在爬树的时候发现了树干上有一大串奇怪的符文,于是好奇的CD就从头到尾看了一遍.看完一遍以后CD觉得,最后一段文字好像很眼熟,好像在前面见过.对于CD来说,一开始看到的符文会印象特 ...

  5. 对async/await的研究

    1. 作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行. 写一个async 函数 async functio ...

  6. darknet-yolov3模型预测框size不正确的原因

    问题描述:预测框的中心位置正常,但是预测的框的width和height不正常. 解决方法:使得训练的配置cfg和测试中cfg的输入width, height, anchorbox保持一致! 问题是我在 ...

  7. linux运维、架构之路-MySQL备份与恢复(四)

    一.备份方式 ①逻辑备份(文件表示:SQL语句) ②物理备份(数据文件的二进制副本) ③基于快照的备份 ④基于复制的备份 二.备份工具 ①mysqldump:原生自带的逻辑备份工具 ②mysqlbin ...

  8. CF G. Orientation of Edges BFS

    来两遍 $BFS,$ 都贪心一下即可. #include <bits/stdc++.h> #define maxn 300009 using namespace std; void set ...

  9. jquery ajax实现文件上传

    test5.html <html> <head> <meta http-equiv="Content-Type" content="text ...

  10. [BZOJ3236][AHOI2013]作业:树套树/莫队+分块

    分析 第一问随便搞,直接说第二问. 令原数列为\(seq\),\(pre_i\)为\(seq_i\)这个值上一个出现的位置,于是可以简化询问条件为: \(l \leq i \leq r\) \(a \ ...