HDU-6070-二分+线段树

Dirt Ratio

Time Limit: 18000/9000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 2522    Accepted Submission(s): 1138
Special Judge

Problem Description

In ACM/ICPC contest, the ‘‘Dirt Ratio‘‘ of a team is calculated in the following way. First let‘s ignore all the problems the team didn‘t pass, assume the team passed Xproblems during the contest, and submitted Y times for these problems, then the ‘‘Dirt Ratio‘‘ is measured as XY. If the ‘‘Dirt Ratio‘‘ of a team is too low, the team tends to cause more penalty, which is not a good performance.


Picture from MyICPC

Little Q is a coach, he is now staring at the submission list of a team. You can assume all the problems occurred in the list was solved by the team during the contest. Little Q calculated the team‘s low ‘‘Dirt Ratio‘‘, felt very angry. He wants to have a talk with them. To make the problem more serious, he wants to choose a continuous subsequence of the list, and then calculate the ‘‘Dirt Ratio‘‘ just based on that subsequence.

Please write a program to find such subsequence having the lowest ‘‘Dirt Ratio‘‘.

Input

The first line of the input contains an integer T(1≤T≤15), denoting the number of test cases.

In each test case, there is an integer n(1≤n≤60000) in the first line, denoting the length of the submission list.

In the next line, there are n positive integers a1,a2,...,an(1≤ai≤n), denoting the problem ID of each submission.

Output

For each test case, print a single line containing a floating number, denoting the lowest ‘‘Dirt Ratio‘‘. The answer must be printed with an absolute error not greater than 10?4.

Sample Input

1
5
1 2 1 2 3

Sample Output

0.5000000000

Hint

For every problem, you can assume its final submission is accepted.

Source

2017 Multi-University Training Contest - Team 4

求一个序列中的连续子序列S,使得 (S中不同元素的个数)/(S的长度)最小化。挺不错的一道题,很考验思维。对于这种最小化的题目很容易往二分上面想,得到 a/b<=k ,我们只要不断二分k,找到下界即可,问题是对于k如何判定是否可行,枚举所有区间显然不靠谱,考虑这个式子  diff(l,r)/(r-l+1)<=k   ->   diff(l,r)+k*l<=k*(r+1), 我们可以枚举一下右端点,然后找到一个最优的左端点判断能否使得这个不等式成立即可,可以用线段树区间修改来维护这个值,结点  u(L,R),保存的就是[L,R]中最小的  diff(l,r)+k*l ,显然l€[L,R]。初始化根节点为k*L,然后遍历右端点的时候更新对应的区间,也就是会对那些区间的diff造成变化。

  

  

  1 #include<iostream>
  2 #include<cstring>
  3 #include<queue>
  4 #include<cstdio>
  5 #include<stack>
  6 #include<set>
  7 #include<map>
  8 #include<cmath>
  9 #include<ctime>
 10 #include<time.h>
 11 #include<algorithm>
 12 #include<bits/stdc++.h>
 13 using namespace std;
 14 #define mp make_pair
 15 #define pb push_back
 16 #define debug puts("debug")
 17 #define LL long long
 18 #define pii pair<int,int>
 19 #define inf 0x3f3f3f3f
 20
 21 #define mid ((L+R)>>1)
 22 #define lc (id<<1)
 23 #define rc (id<<1|1)
 24 #define eps 1e-5
 25 const int _M=60006;
 26 double sum[_M<<2];
 27 int laz[_M<<2];
 28 int a[_M],x[_M],pre[_M];
 29 void pushdown(int id){
 30     if(laz[id]){
 31         laz[lc]+=laz[id];
 32         laz[rc]+=laz[id];
 33         sum[lc]+=laz[id];
 34         sum[rc]+=laz[id];
 35         laz[id]=0;
 36     }
 37 }
 38 void pushup(int id){
 39     sum[id]=min(sum[lc],sum[rc]);
 40 }
 41 void build(int id,int L,int R,double k){
 42     if(L==R){
 43         sum[id]=k*L;
 44         return;
 45     }
 46     build(lc,L,mid,k);
 47     build(rc,mid+1,R,k);
 48     pushup(id);
 49 }
 50 void update(int id,int L,int R,int l,int r){
 51     if(L>=l&&R<=r){
 52         sum[id]++;
 53         laz[id]++;
 54         return ;
 55     }
 56     pushdown(id);
 57     if(l<=mid) update(lc,L,mid,l,r);
 58     if(r>mid) update(rc,mid+1,R,l,r);
 59     pushup(id);
 60 }
 61 double query(int id,int L,int R,int l,int r){
 62     if(L>=l&&R<=r){
 63         return sum[id];
 64     }
 65     pushdown(id);
 66     if(r<=mid) return query(lc,L,mid,l,r);
 67     else if(l>mid) return query(rc,mid+1,R,l,r);
 68     else return min(query(lc,L,mid,l,r),query(rc,mid+1,R,l,r));
 69     pushup(id);
 70 }
 71 bool ok(double k,int n){
 72     memset(sum,0,sizeof(sum));
 73     memset(laz,0,sizeof(laz));
 74     build(1,1,n,k);
 75     for(int i=1;i<=n;++i){
 76         update(1,1,n,pre[i]+1,i);
 77         if(query(1,1,n,1,i)<=k*(i+1)) return 1;
 78     }
 79     return 0;
 80 }
 81 int main(){
 82     int t,n,m,i,j;
 83     scanf("%d",&t);
 84     while(t--){
 85         scanf("%d",&n);
 86         memset(x,0,sizeof(x));
 87         for(i=1;i<=n;++i) {
 88             scanf("%d",a+i);
 89             pre[i]=x[a[i]];
 90             x[a[i]]=i;
 91         }
 92
 93         double l=0,r=1.0;
 94         while(abs(l-r)>eps){
 95             double k=(l+r)/2;
 96             if(ok(k,n)) r=k;
 97             else l=k+eps;
 98         }
 99         printf("%.10f\n",l);
100     }
101     return 0;
102 }                                                                                       

原文地址:https://www.cnblogs.com/zzqc/p/9073925.html

时间: 2024-08-30 08:02:17

HDU-6070-二分+线段树的相关文章

HDU 1542 Atlantis 线段树+离散化+扫描线

题意:给出一些矩形的最上角坐标和右下角坐标,求这些矩形的面积并. NotOnlySuccess 线段树专辑中扫描线模板题,弱智的我对着大大的代码看了一下午才搞懂. 具体见思路见注释=.= #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define lson rt<<1,l,mid #define rson rt<<1|1,mid

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

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6265  Solved: 2060[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

HDU 1828 Picture 线段树+扫描线

题意:给你一些矩形的左上角点的坐标和右下角点的坐标,求周长并 最显而易见的思路就是对于x轴和y轴做两次扫描线,对于负数的坐标进行离散化.每次增加的值是线段变化量的绝对值.具体写法和求面积并的差不多. #include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; #define lson rt << 1 , l , m

HDU 1542 Atlantis(线段树扫描线)

http://acm.hdu.edu.cn/showproblem.php?pid=1542 Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6788    Accepted Submission(s): 2970 Problem Description There are several ancient Greek

POJ 1177/HDU 1828 picture 线段树+离散化+扫描线 轮廓周长计算

求n个图矩形放下来,有的重合有些重合一部分有些没重合,求最后总的不规则图型的轮廓长度. 我的做法是对x进行一遍扫描线,再对y做一遍同样的扫描线,相加即可.因为最后的轮廓必定是由不重合的线段长度组成的,这样理论上是对的 要注意处理高度相同的线段,把底边优先处理(在代码里就是f标记为1的线段),因为若是一个矩形的底边和另一个矩形的上边重合,则这个轮廓肯定不能算 不过POJ和HDU的数据好像都比较弱,我没进行上面的细节处理也AC了,不过一个很简单的数据就会不对,所以还是要处理一下才是真正正确的代码 我

hdu 1542 Atlantis(线段树&amp;扫描线&amp;面积并)

Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6386    Accepted Submission(s): 2814 Problem Description There are several ancient Greek texts that contain descriptions of the fabled i

hdu 1828 Picture(线段树&amp;扫描线&amp;周长并)

Picture Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2578    Accepted Submission(s): 1363 Problem Description A number of rectangular posters, photographs and other pictures of the same shap

hdu 1542 Atlantis(线段树)

Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6899    Accepted Submission(s): 3022 Problem Description There are several ancient Greek texts that contain descriptions of the fabled i

hdu 4864(2) 线段树

对task和machine的yi由小到大进行排序,然后对machine来跟task配对.当machine[].yi >= task[].yi时,就更新线段树,在1-1440上做线段树,线段树存的是task[].xi,同时用用优先队列保存task[].yi:当machine[].yi < task[].yi时,就查找 1到machine[].xi最大的值.如果存在最大值的话,把优先队列里的task[].yi取出来..这样一个machine就匹配到了一个最优的任务.还是看代码好好意会吧,细节挺多的

HDU 1166(线段树单点更新)

HDU 1166 题意:1-n个堡垒,人数在不断变化,多次查询 l-r人数和: 思路:线段树的单点更新: #include<iostream> #include<cstring> #include<cstdio> #include<string> #include<algorithm> #include<cmath> #include<map> #include<vector> #include<queu