题意:
有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
总树高∗(y−x)/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;
}