HDU
来源:互联网 发布:知乎 显微镜原理 编辑:程序博客网 时间:2024/06/01 08:14
题目大意:
就是给你一个区间,让你找出一个子区间,使得区间内不同元素个数/区间长度最大。15组测试数据,每组区间长度6e4。要求答案精确到1e-4。
分析:
首先是要二分查找答案,每次枚举一个答案作为上界,判断是否存在满足条件的区间。这里枚举出答案大概需要20次。
下面说明对于某次枚举的答案mid,如何确定它的可行性:
定义:
dif[ i ][ j ] 表示区间 [ i , j ] 里的不同的元素的个数。
那么我只需要验证:
dif[l,r]/{r-l+1}>=mid;
即:
l*mid + dif[ l ][ r ] >= mid*(r+1);
那么对于每一个即将被判断可行性的 mid ,我就枚举所有的 r ,每次都取 max { i*mid + dif[ i ][ r ] | 1<=i<=r },将它和 mid*(r+1) 进行比较即可判断其可行性。总时间复杂度:O(logw*n*logn);
代码:
#include<bits/stdc++.h>#define MAXN 60050#define inf 0x3f3f3f3fusing namespace std;int test,n,a[MAXN],pre[MAXN],last[MAXN];double tree[MAXN<<2],lazy[MAXN<<2];void Push_up(int x){ tree[x]=min(tree[x<<1],tree[x<<1|1]);}/*void Build(int l,int r,int rt){ if(l==r) { tree[rt]=a[l]; return; } int mid=(l+r)>>1; Build( l , mid , rt<<1 ); Build( mid+1 , r , rt<<1|1 ); Push_up( rt );}*/void Push_down(int rt){ if(lazy[rt]) { lazy[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt]; tree[rt<<1]+=lazy[rt]; tree[rt<<1|1]+=lazy[rt]; lazy[rt]=0; }}void Add_interval(int L,int R,int C,int l,int r,int rt){ if(L<=l&&r<=R) { tree[rt]+=C; lazy[rt]+=C; return; } int mid=(l+r)>>1; Push_down(rt); if(L<=mid)Add_interval(L,R,C,l,mid,rt<<1); if(R>mid)Add_interval(L,R,C,mid+1,r,rt<<1|1); Push_up(rt);}double Qurey_interval(int L,int R,int l,int r,int rt){ if(L<=l&&R>=r)return tree[rt]; int mid=(r+l)>>1; Push_down(rt); double ans=inf; if(L<=mid)ans=min(ans,Qurey_interval(L,R,l,mid,rt<<1)); if(R>mid)ans=min(ans,Qurey_interval(L,R,mid+1,r,rt<<1|1)); Push_up(rt); return ans;}void Add_point(int L,double C,int l,int r,int rt){ if (l==r) { tree[rt]+=C; return; } int mid=(l+r)>>1; Push_down(rt); if (L<=mid) Add_point(L,C,l,mid,rt<<1); else Add_point(L,C,mid+1,r,rt<<1|1); Push_up(rt);}bool run(double mid){ memset(tree,0,sizeof(tree)); memset(lazy,0,sizeof(lazy)); for(int i=1;i<=n;i++) { double flag=mid*(i+1); //cout<<flag<<endl; double t=0; Add_interval(pre[i]+1,i,1,1,n,1); //cout<<pre[i]+1<<" "<<i<<endl; //cout<<tree[1]; Add_point(i,i*mid ,1,n,1); //cout<<tree[1]; t=Qurey_interval(1,i,1,n,1); // cout<<t<<" "<<flag<<endl; if(t<flag) { //cout<<1; return 1; } } //cout<<0; return 0;}int main(){ scanf("%d",&test); while(test--) { memset(last,0,sizeof(last)); memset(pre,0,sizeof(pre)); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); pre[i]=last[a[i]]; last[a[i]]=i; } double mid; double l=0,r=1; for(int i=0;i<30;i++)//为了保险起见,决定循环30次 { mid=(l+r)/2.0; if(run(mid)==1)//如果能跑出来 { r=mid; } else { l=mid; } //cout<<mid<<endl; } printf("%lf\n",mid); }}/*151 2 1 2 3*/