HDU 6070 Dirt Ratio(二分+线段树 17多校第四场)

来源:互联网 发布:苹果mac系统os 编辑:程序博客网 时间:2024/05/29 11:43
  • 题目大意

    给定一个序列a[],让你求出一个子区间内不同元素个数除以区间长度的最小的一个值。n6104,答案精度不高于104

  • 分析

    题目要求的是:

    min{dif[l][r]rl+1}(1lrn,dif[l][r][l,r])

    最暴力的方法通过两层循环来枚举这个区间,外层循环是左端点,内层循环是右端点。
    类似于CF 833B,我们可以用一个数组pre[x]保存在x前面最近的和a[x]相等的数的下标,这样我们可以通过O(1)的复杂度从dif[l][r]推得dif[l][r+1]
    同样类似于CF 833B,这样的O(n2)的复杂度还是会超时
    我们尝试去用一种数据结构来维护这个区间最小值
    我们可以O(1)的复杂度从dif[l][r]推得dif[l][r+1],那么我们可以通过线段树以O(logn)的复杂度从
    dif[1...l][r]推得dif[1...l][r+1],但是这个区间信息是一个带除法运算的表达式我们很难对这个分母进行区间的更新操作
    如果我们能够将除法运算转化成加法运算

    如果我们想到了二分,min{dif[l][r]rl+1}mid是否成立
    变换一下也就是min{dif[l][r]+lmidmid}rmid是否成立
    这样我们就能够对表达式dif[l][r]+lmidmid通过线段树来进行区间更新和查询操作了

  • 代码

#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<cstdlib>#include<queue>#include<map>#include<algorithm>#include<set>#include<stack>using namespace std;const double INF=999999.0;const long long int MOD=998244353;const int MAXN=60005;int T;int n;int a[MAXN];int pre[MAXN];int pos[MAXN];double tree[MAXN*4];double lazy[MAXN*4];void PushUp(int rt)//将延迟标记上推{     tree[rt]=min(tree[rt*2],tree[rt*2+1]);}void PushDown(int rt){    lazy[rt*2]+=lazy[rt];    lazy[rt*2+1]+=lazy[rt];    tree[rt*2]+=lazy[rt];    tree[rt*2+1]+=lazy[rt];    lazy[rt]=0;    return ;}void Build(double x,int l,int r,int rt){    lazy[rt]=0;    if(l==r)    {        tree[rt]=(double)l*x-x;        return ;    }    int m=(l+r)/2;    Build(x,l,m,rt*2);    Build(x,m+1,r,rt*2+1);    PushUp(rt);}void Update(int L,int R,int l,int r,int rt){    if(l>=L && r<=R)    {        lazy[rt]+=1;        tree[rt]+=1;        return ;    }    int m=(l+r)/2;    PushDown(rt);    if(L<=m)Update(L,R,l,m,rt*2);    if(R>m)Update(L,R,m+1,r,rt*2+1);    PushUp(rt);}double Query(int L,int R,int l,int r,int rt){    if(l>=L &&r<=R)    {        return tree[rt];    }    double ans=INF;    PushDown(rt);    int m=(l+r)/2;    if(L<=m)ans=min(ans,Query(L,R,l,m,rt*2));    if(R>m)ans=min(ans,Query(L,R,m+1,r,rt*2+1));    PushUp(rt);    return ans;}bool Check(double x)//判断x作为答案是否可行{    Build(x,1,n,1);    double minm=INF;    for(int i=1;i<=n;i++)    {        Update(pre[i]+1,i,1,n,1);        if(Query(1,i,1,n,1)<=(double)i*x){return 1;}    }    return 0;}void Work(){      double l=0;      double r=1.0;      double m;      while(r-l>0.0000001)      {          m=(l+r)/2;          if(Check(m))r=m;          else l=m;      }      cout<<l<<endl;}int main(){    scanf("%d",&T);    while(T--)    {        memset(pos,0,sizeof(pos));        memset(pre,0,sizeof(pre));        scanf("%d",&n);        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);            pre[i]=pos[a[i]];            pos[a[i]]=i;        }        Work();    }    return 0;}/*151 2 1 2 3*/
原创粉丝点击