• 2019牛客暑期多校训练营(第九场)H(主席树+二分)


    题意:

    (n)个长度分别为(a_i)的竹子,现在有(m)次操作,每次操作给你四个数字(l,r,x,y)。现在你需要将区间([l,r])的竹子砍(y)刀,对于每刀,你需要选择一个高度(h),你需要将区间([l,r])中高度超过(h)的竹子砍掉,最终你需要保证每一刀砍了的高度相同。现在问你第(x)刀需要砍的高度。

    分析:

    根据题意,对于某一个(query(l,r,x,y)),每一刀需要砍掉的高度是固定的,为(frac{sum_{i=l}^{r}a_i}{y})。那么当我们处于第(x)次操作时,一定已经砍掉(frac{sum_{i=l}^{r}a_i*x}{y})高度。而因为我们每次操作砍掉的高度必定是单调增的,因此,我们不妨二分第(x)次需要砍的高度(hei)

    而为了判断当前(hei)是否合法,我们只需要根据当前的高度(hei),计算出之前一共被砍掉的高度(sumhei),最后通过比较(sumhei)(frac{sum_{i=l}^{r}a_i*x}{y})之间的大小即可。

    而要求出(sumhei),我们只需要知道在区间([l,r])中,大于等于高度(hei)的树的个数(size)和权值(val),最后让(val-size*hei)即是答案。而要求出区间内大于某个数的个数以及大小,我们只需要用主席树维护一下即可。

    因为是二分套主席树查询,故整体的时间复杂度为(mathcal{O}(nlogn^2))

    代码:

    #include <bits/stdc++.h>
    #define maxn 200005
    using namespace std;
    typedef long long ll;
    struct ST{
        int l,r,sz;
        ll val;
    }tr[maxn*30];
    int T[maxn],tot,n,m,a[maxn];
    ll sum[maxn];
    int update(int l,int r,int pre,int pos,int val){
        int rt=++tot;
        tr[rt]=tr[pre];
        tr[rt].val+=val;
        tr[rt].sz++;
        if(l==r) return rt;
        int mid=(l+r)>>1;
        if(pos<=mid) tr[rt].l=update(l,mid,tr[rt].l,pos,val);
        else tr[rt].r=update(mid+1,r,tr[rt].r,pos,val);
        return rt;
    }
    void query(int l,int r,int rt,int pre,int K,int &ans1,ll &ans2){
        if(l==r){
            ans1+=tr[rt].sz-tr[pre].sz;
            ans2+=tr[rt].val-tr[pre].val;
            return ;
        }
        int mid=(l+r)>>1;
        if(K<=mid){
            query(l,mid,tr[rt].l,tr[pre].l,K,ans1,ans2);
            ans1+=tr[tr[rt].r].sz-tr[tr[pre].r].sz;
            ans2+=tr[tr[rt].r].val-tr[tr[pre].r].val;
        }else query(mid+1,r,tr[rt].r,tr[pre].r,K,ans1,ans2);
        return;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
            T[i]=update(1,100005,T[i-1],a[i],a[i]);
        }
        for(int i=1;i<=m;i++){
            int l,r,x,y;
            scanf("%d%d%d%d",&l,&r,&x,&y);
            double tmp=1.0*(sum[r]-sum[l-1])/y*x;
            double ll=0,rr=100005;
            for(int j=0;j<100;j++){
                double mid=(ll+rr)/2;
                int tt=ceil(mid),res1=0;
                long long res2=0;
                query(1,100005,T[r],T[l-1],tt,res1,res2);
                double ans=res2-res1*mid;
                if(ans<tmp) rr=mid;
                else ll=mid;
            }
            printf("%.8f
    ",ll);
        }
        return 0;
    }
    
  • 相关阅读:
    设计模式-工厂设计模式
    Spring Batch BATCH_JOB_SEQ 出现死锁问题
    windows 安装 jenkins 自动化构建部署至linux服务器上
    Git安装
    MAVEN(一) 安装和环境变量配置
    Jenkins 安装
    jenkins操作
    linux firewalld 防火墙操作命令
    【Azure Redis 缓存】Azure Redis读写比较慢/卡的问题排查
    【Azure 服务总线】向服务总线发送消息时,返回错误代码Error code : 50009
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11360770.html
Copyright © 2020-2023  润新知