【2019牛客暑期多校训练营 第九场 H题】【Cutting Bamboos】【主席树+二分】

题目链接:
https://ac.nowcoder.com/acm/contest/889/H
题意:
N颗竹子,M个询问操作,每次询问给出 l , r , x , y l,r,x,y l,r,x,y,问区间 [ l , r ] [l,r] [l,r]内的竹子都切到什么高度 h h h时,使得切掉的竹子高度之和为原来高度之和的 x / y x/y x/y,注意这里只有高度大于 h h h的竹子才会割,也就是说,如果让 h = 2 h=2 h=2,你是不能将1米高的竹子变为2米高的,还有这里只是询问,并不会改变竹子的高度。
题解:
算是一个主席树模板题了。
这里区间内竹子总高度可以用前缀和处理,即 s u m = p r e [ r ] − p r e [ l − 1 ] sum=pre[r]-pre[l-1] sum=pre[r]pre[l1]
大于高度 h h h的竹子数量为 s u m _ n sum\_n sum_n,大于高度 h h h的竹子高度总和为 s u m _ h sum\_h sum_h
所以这里其实就是求最大的 h h h使得:
s u m _ h − s u m _ n ∗ h &lt; s u m ∗ x / y sum\_h-sum\_n *h&lt;sum*x/y sum_hsum_nh<sumx/y
砍掉的竹子高度 = = =大于h的高度总和 − - 没砍掉的数量 ∗ * 高度
所以关键就是求 s u m _ n sum\_n sum_n s u m _ h sum\_h sum_h,这里可以用主席树来处理
查询区间内大于某个数n的元素个数和元素值之和
而这里的求 h h h显然可以用二分来求(有分界)
代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define m (l+r)/2
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 2e5 + 10;
 
int N, M;
ll pre[MAX];
 
int cnt, rt[MAX << 5], lc[MAX << 5], rc[MAX << 5], num[MAX << 5];
ll sum[MAX << 5];
 
int insert(int pre, int l, int r, int k) {
    int now = ++cnt;
    lc[now] = lc[pre], rc[now] = rc[pre];
    num[now] = num[pre] + 1, sum[now] = sum[pre] + k;
    if (l < r) {
        if (k <= m)lc[now] = insert(lc[pre], l, m, k);
        else rc[now] = insert(rc[pre], m + 1, r, k);
    }
    return now;
}
 
int sum_n;
ll sum_v;
 
void query(int now, int pre, int l, int r, int k) {//查询sum_h(这里的sum_v)和sum_n
    if (k <= l) {
        sum_n += num[now] - num[pre];
        sum_v += sum[now] - sum[pre];
        return;
    }
    if (k <= m)query(lc[now], lc[pre], l, m, k);
    query(rc[now], rc[pre], m + 1, r, k);
}
 
double cal(int l, int r, double h) {//计算总共砍了多少高度
    sum_n = 0, sum_v = 0;
    query(rt[r], rt[l - 1], 1, 100001, (int)ceil(h));
    return sum_v - sum_n * h;
}
 
int main() {
#ifdef ACM_LOCAL
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
#endif
    cnt = 0;
    scanf("%d%d", &N, &M);
    for (int i = 1; i <= N; i++) {
        scanf("%lld", &pre[i]);
        rt[i] = insert(rt[i - 1], 1, 100001, pre[i]);
        pre[i] += pre[i - 1];
    }
    while (M--) {
        int ql, qr, x, y;
        scanf("%d%d%d%d", &ql, &qr, &x, &y);
        double req = 1.0 * (pre[qr] - pre[ql - 1]) / y * x;
        double l = 0, r = 100000;
        int T = 50;
        while (T--) {
            double mid = (l + r) / 2.0;
            if (cal(ql, qr, mid) < req)r = mid;
            else l = mid;
        }
        printf("%.10lf\n", l);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值