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
 

    求一个序列中的连续子序列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 }                                                                                       

 

posted @ 2018-05-22 20:33  *zzq  阅读(239)  评论(0)    收藏  举报