zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    iphone、UI设计和PKM的混谈
    社保名单核对的难题
    LVDS技术原理和设计简介
    系统级芯片设计语言和验证语言的发展
    芯片封装(Chip Package)类型70种
    数字电平标准 TTL CMOS ECL LVDS CML...
    什么是施密特触发器(Schmitt Trigger)?
    组合逻辑电路中竞争冒险的分析
    ASIC设计流程(ASIC design flow)
    这18条背下来没人敢和你忽悠CPU
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11360770.html
Copyright © 2011-2022 走看看