题面

传送门

此题的题意不是很清晰,要注意的一点是在区间[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. 前端之JavaScript:JS之DOM对象一

    js之DOM对象一 一.什么是HTML  DOM HTML  Document Object Model(文档对象模型) HTML DOM 定义了访问和操作HTML文档的标准方法 HTML DOM 把 ...

  2. hdu 3572 : Task Schedule (网络流)

    题目链接 题意: 有M个机器,N个任务 对第i个任务,需要在[Si,Ei]这段时间内恰有Pi天被process 每天最多有M个机器同时工作 每一天,一个任务若被process,那么它恰占用一个机器. ...

  3. Vuex-全局状态管理【传递参数】

    src根目录 新建store文件夹,新建index.js 作为入口 在store文件夹中 新建modules文件夹 modules文件夹中,新建 a.js b.js 2个文件 a.js const s ...

  4. python代码执行bash命令 -- python3 cook book

    python代码执行bash命令相关 -- python3 cook book refer: https://python3-cookbook.readthedocs.io/zh_CN/latest/ ...

  5. 举个例子去理解vuex(状态管理),通俗理解vuex原理,通过vue例子类比

    通俗理解vuex原理---通过vue例子类比   本文主要通过简单的理解来解释下vuex的基本流程,而这也是vuex难点之一. 首先我们先了解下vuex的作用vuex其实是集中的数据管理仓库,相当于数 ...

  6. Java中日期

    package com.shiro.springbootshiro; import java.text.SimpleDateFormat; import java.util.Date; /** * 作 ...

  7. android 常用框架总结(转载)

    原文:https://blog.csdn.net/weixin_44702125/article/details/89886948

  8. springboot集成mongoDB需要认证

    报错: Mon Nov 25 01:09:48 CST 2019 There was an unexpected error (type=Internal Server Error, status=5 ...

  9. flex兼容问题

    display:flex作为C3的新属性,还是有的浏览器不支持的,那下面我们就来说一下他的兼容写法 .box{ display: -webkit-box; /* 老版本语法: Safari, iOS, ...

  10. 笨办法学Python(learn python the hard way)--练习程序41

    下面是练习41,基于python3 #ex41.py 1 #打印文档字符串 print(函数名.__doc__) 2 from sys import exit 3 from random import ...