hdu6070 二分+线段树

来源:互联网 发布:阿里云cdn添加域名 编辑:程序博客网 时间:2024/06/06 03:00

题意: 区间价值为    区间元素种类数  /  区间长度    问最小价值的区间是?


思路:直接求解很困难,考虑二分答案判断,注意这题的关键是将二分答案后的不等式进行变换,如官方题解。二分答案 mid,检验是否存在一个区间满足 size(l,r)/(r−l+1) ≤ mid,也就是 size(l, r) + mid × l ≤mid × (r + 1)。

之后的问题就很好解决了,枚举右端点,开一颗线段树来维护到当前节点的区间不同数(类似树状数组求区间不同数的想法来加入一个新点,主要是通过一个数组维护上一次出现的位置)+  mid × l ,这样就可以轻松判断是否存在解了。



#include<bits/stdc++.h>const int maxn = 6e4+50;const double esp = 1e-5;using namespace std;int a[maxn],last[maxn],pre[maxn],n;double tree[maxn<<2],c[maxn<<2];void pushup(int rt){tree[rt] = min(tree[rt<<1],tree[rt<<1|1]);}void pushdown(int rt){    if(c[rt]>0)    {        c[rt<<1]+=c[rt];        c[rt<<1|1]+=c[rt];        tree[rt<<1]+=c[rt];        tree[rt<<1|1]+=c[rt];        c[rt] = 0;    }}void Build(int l,int r,int rt,double mid){    c[rt] =0;    if(l==r)    {        tree[rt] = mid*l*1.0;        return ;    }    int Mid = (l+r)>>1;    Build(l,Mid,rt<<1,mid);    Build(Mid+1,r,rt<<1|1,mid);    pushup(rt);}void updata(int l,int r,int rt,int L,int R,double x){    if(L<=l&&r<=R)    {        tree[rt]+=x;        c[rt]+=x;        return ;    }    pushdown(rt);    int mid = (l+r)>>1;    if(L<=mid)    {        updata(l,mid,rt<<1,L,R,x);    }    if(R>mid)    {        updata(mid+1,r,rt<<1|1,L,R,x);    }    pushup(rt);}double quary(int l,int r,int rt,int L,int R){    if(L<=l&&r<=R)    {        return tree[rt];    }    pushdown(rt);    int mid = (l+r)>>1;    double res = 1e9;    if(L<=mid)    {        res = min(res,quary(l,mid,rt<<1,L,R));    }    if(R>mid)    {        res = min(res,quary(mid+1,r,rt<<1|1,L,R));    }    return res;}bool check(double mid){    Build(1,n,1,mid);    for(int i=1;i<=n;i++)    {        updata(1,n,1,pre[i]+1,i,1.0);        if(quary(1,n,1,1,i)<=mid*(i+1.0))return true;    }    return false;}int main(){    int t;cin>>t;    while(t--)    {        scanf("%d",&n);        memset(last,0,sizeof(last));        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);            if(last[a[i]])            {                pre[i] = last[a[i]];            }            else pre[i] = 0;            last[a[i]] = i;        }        double l = 0,r = 1.0,mid,ans;        while(r-l>esp)        {            mid = (l+r)/2.0;            if(check(mid))r = (ans=mid)-esp;            else l = mid+esp;        }        printf("%lf\n",ans);    }    return 0;}


原创粉丝点击