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

题目链接

题解:

我们可以二分砍到的高度,那么怎么求 L 到 R 区间内一个高度能砍掉的竹子呢?那么可以用一颗可持久化线段树来使区间的高度变得有序。

具体的,用主席树维护区间的高度之和与区间每个高度出现的次数。每次可以计算出高于二分高度的和 sum 与出现次数 num,那么剪掉的部分就是 sum - 二分的高度 * num 。那么就直接二分了。

代码:

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
#define int ll
#define PI acos(-1.0)
#define INF 0x3f3f3f3f3f3f3f3f
#define P pair<int, int>
#define fastio ios::sync_with_stdio(false), cin.tie(0)
const int mod = 998244353;
const int M = 1000000 + 10;
const int N = 200000 + 10;

int pres[N];
struct node {
    int ls, rs;
    int sum, num;
} tree[N<<5];
int rt[N], cnt;

void build(int &now, int l = 1, int r = 100000)
{
    now = ++cnt;
    tree[now].num = tree[now].sum = 0;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(tree[now].ls, l, mid);
    build(tree[now].rs, mid + 1, r);
}

void add(int &now, int pre, int val, int l = 1, int r = 100000)
{
    now = ++cnt;
    tree[now] = tree[pre];
    if(l == r) {
        tree[now].num ++;
        tree[now].sum += val;
        return ;
    }
    int mid = (l + r) >> 1;
    if(val <= mid) add(tree[now].ls, tree[pre].ls, val, l, mid);
    else add(tree[now].rs, tree[pre].rs, val, mid + 1, r);

    tree[now].num = tree[tree[now].ls].num + tree[tree[now].rs].num;
    tree[now].sum = tree[tree[now].ls].sum + tree[tree[now].rs].sum;
}

int query_num(int p, int q, int L, int R = 100000, int l = 1, int r = 100000)
{
    if(L <= l && r <= R) return tree[q].num - tree[p].num;
    int mid = (l + r) >> 1, ans = 0;
    if(L <= mid) ans += query_num(tree[p].ls, tree[q].ls, L, R, l, mid);
    if(mid < R) ans += query_num(tree[p].rs, tree[q].rs, L, R, mid + 1, r);
    return ans;
}

int query_sum(int p, int q, int L, int R = 100000, int l = 1, int r = 100000)
{
    if(L <= l && r <= R) return tree[q].sum - tree[p].sum;
    int mid = (l + r) >> 1, ans = 0;
    if(L <= mid) ans += query_sum(tree[p].ls, tree[q].ls, L, R, l, mid);
    if(mid < R) ans += query_sum(tree[p].rs, tree[q].rs, L, R, mid + 1, r);
    return ans;
}

signed main()
{
    int n, q;
    scanf("%lld %lld", &n, &q);
    build(rt[0]);
    for(int i = 1, h; i <= n; i ++) {
        scanf("%lld", &h);
        pres[i] = pres[i - 1] + h;
        add(rt[i], rt[i - 1], h);
    }
    for(int i = 1, l, r, x, y; i <= q; i ++) {
        scanf("%lld %lld %lld %lld", &l, &r, &x, &y);
        double ql = 0, qr = 100010;
        while(ql + 1e-7 < qr) {
            double mid = (ql + qr) / 2;
            int qmid = ceil(mid);
            int qsum = query_sum(rt[l - 1], rt[r], qmid);
            int qnum = query_num(rt[l - 1], rt[r], qmid);
            if(qsum - qnum * mid >= 1.0 * (pres[r] - pres[l - 1]) * x / y) ql = mid;
            else qr = mid;
        }
        printf("%.10lf\n", qr);
    }
}

/*

  Rejoicing in hope, patient in tribulation.

*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值