主席树+二分-2019暑假牛客第9场-H-Cutting Bamboos

题面:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路:
使用主席树快速查询区间内大于某个数的个数与总和。然后二分答案。

#include<bits/stdc++.h>
#define per(i,a,b) for(int i = (a);i <= (b);++i)
#define rep(i,a,b) for(int i = (a);i >= (b);--i)
using namespace std;
typedef long long LL;
const double eps = 1e-8;
const int maxn = 2e5 + 10;
int n = 0,q = 0;
int h[maxn];
int tim = 0;
struct Node{
	int l,r,cnt;
	LL sum;
}tree[40*maxn];
int root[maxn];//记录各个时间点的线段树的根节点位置
LL pre_sum[maxn];
LL cut_num = 0,cut_sum = 0;//分别记录该高度的数量和区间和
void build(int l,int r,int& cur){
	tree[cur].sum = tree[cur].l = tree[cur].r,tree[cur].cnt = 0;
	cur = ++tim;
	if(l == r){
		return ;
	}
	int mid = (l + r) >> 1;
	build(l,mid,tree[cur].l);
	build(mid+1,r,tree[cur].r);
}
void update(int l,int r,int& cur,int pre,int pos){
	cur = ++tim;//记录新的时间点的线段树
	tree[cur] = tree[pre];
	++tree[cur].cnt; tree[cur].sum += pos;
	if(l == r){
		return ;
	}
	int mid = (l + r) >> 1;
	if(pos <= mid){
		update(l,mid,tree[cur].l,tree[pre].l,pos);
	}else{
		update(mid+1,r,tree[cur].r,tree[pre].r,pos);
	}
}
void query(int l,int r,int ql,int qr,int pos){
	if(pos > r){
		return ;
	}
	if(pos <= l){//如果查询高度,小于区间最小高度,那么便是该区间所有的数都大于pos
		cut_num += tree[qr].cnt - tree[ql].cnt;
		cut_sum += tree[qr].sum - tree[ql].sum;
		return ;
	}
	int mid = (l + r ) >> 1;
	query(l,mid,tree[ql].l,tree[qr].l,pos);
	query(mid+1,r,tree[ql].r,tree[qr].r,pos);
}
void init(){
	tim = 0;
}
int main(){
	scanf("%d %d",&n,&q);
	init();
	int maxv = -1;
	per(i,1,n){
		scanf("%d",&h[i]);
		maxv = max(maxv,h[i]);
		pre_sum[i] = pre_sum[i-1] + h[i];
	}
	per(i,1,n){
		update(1,maxv,root[i],root[i-1],h[i]);
	}
	while(q--){
		int ql = 0,qr = 0,x = 0,y = 0;
		scanf("%d %d %d %d",&ql,&qr,&x,&y);
		double H = 1.0*(pre_sum[qr] - pre_sum[ql-1]) * x / y;
		// double H = 1.0 * (tree[root[qr]].sum - tree[root[ql-1]].sum) * x / y;
		double l = 0,r = 1e5;
		
		while(fabs(r-l) > eps){//二分找答案
			cut_num = 0,cut_sum = 0;
			double mid = (l + r) / 2;
			query(1,maxv,root[ql-1],root[qr],ceil(mid));
			if(cut_sum - 1.0*cut_num * mid <= H){//如果砍掉的比应该砍掉的小,那么就多看一点,所以剩下的高度mid,就要变小
				r = mid;
			}else{
				l = mid;
			}
		}
		printf("%.15lf\n",r);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值