/** 题目:hdu6070 Dirt Ratio 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6070 题意:给定n个数,求1.0*x/y最小是多少。x表示一段区间内不同数字的个数,y表示区间长度。 思路:二分+线段树 二分答案x/y。 找一段区间满足 size(l,r)/(r-l+1) <= mid , size(l,r)表示[l,r]内不同数的个数。 size(l,r)<=mid(r-l+1) => size(l,r)+mid*l<=mid*(r+1); 用线段树维护size(l,r)+mid*l的最小值。每一个节点存储size(l,r)+mid*l,枚举r,更新当前a[r]到不包含该数字的节点值+1 */ #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; #define lson L,m,rt<<1 #define rson m+1,R,rt<<1|1 const double eps = 1e-4; const int N = 6e4+10; double add[N<<2], sum[N<<2]; int last[N], a[N]; void pushup(int rt) { sum[rt] = min(sum[rt<<1],sum[rt<<1|1]); } void pushdown(int rt) { sum[rt<<1] += add[rt]; sum[rt<<1|1] += add[rt]; add[rt<<1] += add[rt]; add[rt<<1|1] += add[rt]; add[rt] = 0; } void update(int l,int r,int L,int R,int rt,double d) { if(l<=L&&R<=r){ sum[rt]+=d; add[rt]+=d; return ; } if(add[rt]>eps) pushdown(rt); int m = (L+R)/2; if(r<=m) update(l,r,lson,d); else if(l>m) update(l,r,rson,d); else{ update(l,r,lson,d); update(l,r,rson,d); } pushup(rt); } double query(int l,int r,int L,int R,int rt) { if(l<=L&&R<=r){ return sum[rt]; } if(add[rt]>eps) pushdown(rt); int m = (L+R)/2; double mis; if(r<=m) mis = query(l,r,lson); else if(l>m) mis = query(l,r,rson); else mis = min(query(l,r,lson),query(l,r,rson)); pushup(rt); return mis; } int main() { //freopen("C:\Users\accqx\Desktop\in.txt","r",stdin); int T, n; cin>>T; while(T--) { scanf("%d",&n); for(int i = 1; i <= n; i++) scanf("%d",&a[i]); double hi = 1, lo = 0, mid; double temp; while(hi-lo>eps){ mid = (hi+lo)/2; memset(add, 0, sizeof add); memset(sum, 0, sizeof sum); memset(last, 0, sizeof last); int flag = 0; for(int i = 1; i <= n; i++){ update(last[a[i]]+1,i,1,n,1,1); update(i,i,1,n,1,mid*i); temp = query(1,i,1,n,1); if(temp<=mid*(i+1)){ flag = 1; break; } last[a[i]] = i; } if(flag) hi = mid; else lo = mid; } //cout<<mid<<endl; printf("%.10f ",mid); } return 0; }