题目链接:
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[l−1]
记大于高度
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
<
s
u
m
∗
x
/
y
sum\_h-sum\_n *h<sum*x/y
sum_h−sum_n∗h<sum∗x/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;
}