2019暑假牛客多校赛第九场H.Cutting Bamboos (主席树+二分)

题意:
有n条柱子,高度为 a i a_i ai ,我们有 q q q次操作。在l到r的范围内砍 y y y次,将所有的树高都砍为0,但是保证每一刀砍出来的长度(砍除树高于该高度的和)都是相同的。问你第 x x x次砍的时候砍的高度在哪里。有精度误差。每次只对本次操作有影响,操作完后,树回到原来的高度。
思路:
在求之前,我们先要知道每次可以砍多少,总的树高除于y就是每砍一次的后少的长度,然后砍到第 x x x次时剩下的树高就是 总 树 高 ∗ ( y − x ) / y 总树高*(y-x)/y (yx)/y
要求砍到第 x x x刀时要求的高度,可以对高度进行二分查找,当目前剩下的总树高多于应该的树高时,往下二分。对于如何求当前二分高度的总树高,可以用主席树找出低于目前高度的树的总高度,再加上高于目前高度的树的数目乘以目前高度。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const double eps = 1e-8;
const int N = 2e5 + 10;
int n, q, cnt, root[N], ty;
ll xiao, xnum;
struct node {
    int l, r;
    ll num, sum;
} zxs[N * 40];
void add(int l, int r, int pre, int &now, int pos) {
    zxs[++cnt] = zxs[pre], now = cnt, zxs[cnt].num++, zxs[cnt].sum += pos;
    if(l == r)
        return;
    int m = (l + r) >> 1;
    if(pos <= m)
        add(l, m, zxs[pre].l, zxs[now].l, pos);
    else
        add(m + 1, r, zxs[pre].r, zxs[now].r, pos);
}
void query(int pl, int l, int r, int L, int R) {
    if(r <= pl) {
        xiao += zxs[R].num - zxs[L].num;
        xnum += zxs[R].sum - zxs[L].sum;
        return;
    }
    int m = (l + r) >> 1;
    if(pl >= l)
        query(pl, l, m, zxs[L].l, zxs[R].l);
    if(pl > m)
        query(pl, m + 1, r, zxs[L].r, zxs[R].r);
}
int main() {
    scanf("%d%d", &n, &q);
    for(int i = 1; i <= n; i++)
        scanf("%d", &ty), add(1, N, root[i - 1], root[i], ty);
    while(q--) {
        int l, r, x, y;
        scanf("%d%d%d%d", &l, &r, &x, &y);
        double li = 0.0, ri = 1000000.0;
        double sx = (0.0 + zxs[root[r]].sum - zxs[root[l - 1]].sum) * (y - x) / y;//应该剩下的
        while(fabs(ri - li) > eps) {
            xiao = xnum = 0;
            double mid = (li + ri) / 2;
            query((int)mid, 1, N, root[l - 1], root[r]);
            double ns = xnum + (r - l + 1 - xiao) * mid; //砍掉高于这个mid的剩下的长度
            if(ns > sx)
                ri = mid;
            else
                li = mid;
        }
        printf("%.15f\n", li);
    }
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值