2019 牛客 多校9 H Cutting Bamboos(主席树)

链接

题意:

有n个竹子,每个竹子有自己的高度 h [ i ] h[i] h[i] h [ i ] ∈ [ 1 , 100000 ] h[i]\in[1,100000] h[i][1,100000]
有q个询问,格式为l r x y
询问l到r区间内割y刀,第x刀的高度在哪。


思路:

以竹子的下标作为版本建主席树,把高度作为另一维(也就是主席树上的l,r),根据高度维护竹子的个数和高度和。
最后二分切的高度,记一个二分的高度h,然后找出小于等于h的竹子有x个,以及这些竹子的高度和sum_h1。区间内总竹子个数减去小于等于h的竹子的个数就是高于h的竹子个数了,也就是r-l+1-x。最后剩下的竹子高度之和为sum_h1+((r-l+1)-x)*k。然后拿这个去跟直接计算出的高度比较( ( ∑ i ∈ [ l , r ] h [ i ] ) / y ∗ ( y − x ) (\sum_{i\in[l,r]}h[i])/y*(y-x) (i[l,r]h[i])/y(yx)),如果剩下来的大于这个值,那么h还要再往下放。

(貌似可以直接计算出来,就不用二分了,复杂度降一个log)


参考代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
ll sh[N];
int root[N],lson[N*20],rson[N*20],tot;
ll sum[N*20],sz[N*20];
void update(int last,int cur,int l,int r, int k){
    rson[cur]=rson[last];
    lson[cur]=lson[last];
    sum[cur]=sum[last]+k;
    sz[cur]=sz[last]+1;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(k<=mid)update(lson[last],lson[cur]=++tot,l,mid,k);
    else update(rson[last],rson[cur]=++tot,mid+1,r,k);
}

ll a_sum,a_num;

void query(int last,int cur,int l,int r,int m){
    if(m>=r){
        a_sum+=sum[cur]-sum[last];
        a_num+=sz[cur]-sz[last];
        return;
    }
    int mid=(l+r)>>1;
    if(l<=m)query(lson[last],lson[cur],l,mid,m);
    if(mid+1<=m)query(rson[last],rson[cur],mid+1,r,m);
}

int main(){
    int n,q;
    scanf("%d%d",&n,&q);
    int h;
    tot=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&h);
        update(root[i-1],root[i]=++tot,1,100000,h);
        sh[i]=sh[i-1]+1ll*h;
    }
    int l,r,x,y;
    for(int i=1;i<=q;i++){
        scanf("%d%d%d%d",&l,&r,&x,&y);
        double li=0,ri=100000.0,eps=1e-7,mid;
        while (fabs(ri-li)>eps){
            mid=(li+ri)/2.0;
            a_sum=0;
            a_num=0;
            query(root[l-1],root[r],1,100000,(int)mid);
            a_num=1ll*(r-l+1)-a_num;
            double s=mid*a_num+1.0*a_sum;
            double cnt=1.0*(sh[r]-sh[l-1])/y*(y-x);
            if(cnt<s)ri=mid;
            else li=mid;
        }
        printf("%.7f\n",li);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值