题解:
我们可以二分砍到的高度,那么怎么求 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.
*/