HDU 6070 Dirt Ratio

链接:http://acm.hdu.edu.cn/showproblem.php?pid=6070

题意:给出一个数列,定义len为某一区间长度,定义size为区间数字种类数,求所有区间最小的size/len;

思路:线段树+二分答案

对于所有区间的l,r而言,所求为size(l,r)/(r-l+1)<=mid(二分到当前的答案),可以化为size(l,r)+mid*l<=(r+1)*mid;

然后就是线段树的维护,我们对线段树的叶子定义为所在点(即L)到当前枚举的R之间这个区间的size(l,r)+mid*l,线段树

就维护每个区间最小的size(l,r)+mid*l,就好了;又看得出来对于以上式子当其他值不变只是变mid时,随着mid增加右边比

左边增加的多(r+1>l),而我们要找最小的mid,所以只要满足以上,那么就找更小的mid来试就好;精度的话到1e-5就可以过了;

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define L l,m,u<<1
#define R m+1,r,u<<1|1
typedef long long LL;
const int N=6e4+10;
const double esp=1e-5;
double add[N<<2],Min[N<<2];
int last[N],a[N],n;

void pushUp(int u)
{
    Min[u]=min(Min[u<<1],Min[u<<1|1]);
}
void pushDown(int u)
{
    if(add[u])
    {
        add[u<<1]+=add[u];
        add[u<<1|1]+=add[u];
        Min[u<<1]+=add[u];
        Min[u<<1|1]+=add[u];
        add[u]=0;
    }
}
void build(int l,int r,int u,double v)
{
    add[u]=0;
    if(l==r)
    {
       Min[u]=l*v;
       return;
    }
    int m=(l+r)>>1;
    build(L,v);
    build(R,v);
    pushUp(u);
}
void update(int l1,int r1,int c,int l,int r,int u)
{
    if(l1<=l && r<=r1)
    {
        add[u]+=c;
        Min[u]+=c;
        return;
    }
    pushDown(u);
    int m=(r+l)>>1;
    if(l1<=m)
        update(l1,r1,c,L);
    if(m<r1)
        update(l1,r1,c,R);
    pushUp(u);
}
double query(int l1,int r1,int l,int r,int u)
{
    if(l1<=l && r<=r1)
    {
        return Min[u];
    }
    pushDown(u);
    int m=(l+r)>>1;
    double ret=1e5;
    if(l1<=m)
        ret=min(ret,query(l1,r1,L));
    if(m<r1)
        ret=min(ret,query(l1,r1,R));
    return ret;
}

bool check(double m)
{
    build(1,n,1,m);
    memset(last,0,sizeof(last));
    for(int i=1;i<=n;i++)
    {
        update(last[a[i]]+1,i,1,1,n,1);
        last[a[i]]=i;
        if(query(1,i,1,n,1)<=m*(i+1))return true;
    }
    return false;
}

int main()
{
   // freopen("input.txt","r",stdin);
    int T;
    for(scanf("%d",&T);T;T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        double l=0.0,r=1.0,m,ans;
        while(r-l>esp)
        {
           m=(l+r)/2;
           if(check(m))r=(ans=m);
           else        l=m;
        }
        printf("%.9lf\n",ans);
    }
    return 0;
}

 

posted @ 2017-08-04 18:07  MeowMeowMeow  阅读(134)  评论(0编辑  收藏  举报