2019牛客暑期多校训练营(第九场) H Cutting Bamboos 主席树+二分

链接:https://ac.nowcoder.com/acm/contest/889/H
来源:牛客网
 

时间限制:C/C++ 5秒,其他语言10秒
空间限制:C/C++ 262144K,其他语言524288K
Special Judge, 64bit IO Format: %lld

题目描述

There are n bamboos arranged in a line. The i-th bamboo from the left has height hih_{i}hi​.

You are given q queries of the type (l, r, x, y). For each query (l, r, x, y) we consider only the l-th to r-th bamboo inclusive. We want to make y horizontal cuts through these bamboos, such that after each cut, the total length of bamboo cut is the same, and after all y cuts there is no bamboo left. For example, if there are 3 bamboos of height 3, 4, 5 respectively and y = 4. The first cut should be at height 3, after which the heights of the bamboos are 3, 3, 3 respectively and the total amount of bamboo cut is 0 + 1 + 2 = 3. Then, the next 3 cuts should be at height 2, 1, 0 respectively. You want to find out what is the height the x-th cut is performed.

Note that after each query, the bamboos are not actually cut, so the heights of the bamboos remain constant after each query.

 

输入描述:

The first line of input contains two space-separated integers n, q (1 <= n <= 200000, 1 <= q <= 100000).

The next line of input contains n space-separated integers, the i-th of which denotes hi, the height of the i-th bamboo (1 <= hi <= 100000).

The next q lines of input contains 4 space-separated integers each, denoting l, r, x, y (1 <= l <= r <= n, 1 <= x <= y <= 109).

输出描述:

Output q lines of real numbers, the i-th line contains the answer to the i-th query. Your answer will be accepted if its absolute or relative error is less than 10-6.

示例1

输入

5 4
3 5 1 7 4
2 4 3 5
1 4 4 9
1 3 1999 101111
2 2 1 1

输出

2.100000005215406
2.629629638046026
4.822066854685545
0.000000026077032

说明

For the first query, we only consider the bamboos of height 5, 1, 7.

The first cut should be at height 4.7, the total amount of bamboo obtained is 0.3 + 0 + 2.3 = 2.6.

The second cut should be at height 3.4, the total amount of bamboo obtained is 1.3 + 0 + 1.3 = 2.6.

The third cut should be at height 2.1, the total amount of bamboo obtained is 1.3 + 0 + 1.3 = 2.6.

The fourth cut should be at height 13/15, the total amount of bamboo obtained is 37/30 + 2/15 + 37/30 = 2.6.

The fifth cut should be at height 0, the total amount of bamboo obtained is 13/15 + 13/15 + 13/15 = 2.6.

Note that the output values are not exact, but are within the precision requirements.

题意:给你长度为n个竹子,q个询问,每个询问给你l,r,x,y,求[l,r]砍x次后的最高的高度。一共砍y次,y次后区间内竹子高度为0,每一次砍的竹子的长度是一样的。(每个询问都是假设砍竹子,并不是真的砍)。

思路:主席树中每一棵都是权值线段树,表示高度,主席树维护高度和以及每个高度的数量。维护一个前缀和,那么区间[l,r]需要砍掉的高度为(sum[r]-sum[l-1])*x/y(先乘后除提高精度).二分答案mid,那么大于mid的所有竹子都要砍到mid。主席树查询[l,r]中高度大于等于ceil(mid)的竹子的高度sum,以及它们的数量num,那么当前砍掉的高度为sum-num*mid,与需要砍掉的高度比较,二分找最符合的即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps=1e-9;
const int maxn=2e5+100;
const int maxm=1e5+100;
ll sum[maxn];
int n,q,maxh,cnt,a[maxn],root[maxn];
struct node{
    int l;
    int r;
    int num;
    ll sum;
}tree[maxm*40];
struct Node{
    ll sum;
    int num;
    Node()
    {
        sum=0;
        num=0;
    }
    Node(ll sum,int num):sum(sum),num(num){}
};
void pushup(int cur)
{
    tree[cur].sum=tree[tree[cur].l].sum+tree[tree[cur].r].sum;
    tree[cur].num=tree[tree[cur].l].num+tree[tree[cur].r].num;
}
void build(int &cur,int l,int r)
{
    cur=++cnt;
    tree[cur].num=0;
    tree[cur].sum=0;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(tree[cur].l,l,m);
    build(tree[cur].r,m+1,r);
}
void update(int &now,int last,int l,int r,int tar)
{
    now=++cnt;
    tree[now]=tree[last];
    if(l==r)
    {
        tree[now].num++;
        tree[now].sum+=tar;
//      printf("%lld____\n",tree[now].sum);
        return ;
    }
    int m=(l+r)>>1;
    if(tar<=m) update(tree[now].l,tree[last].l,l,m,tar);
    else update(tree[now].r,tree[last].r,m+1,r,tar);
    pushup(now);
}
Node query(int x,int y,int L,int R,int l,int r)
{
    if(L<=l&&r<=R) return Node(tree[y].sum-tree[x].sum,tree[y].num-tree[x].num);
    Node tmp1,tmp2;
    int m=(l+r)>>1;
    if(L<=m) tmp1=query(tree[x].l,tree[y].l,L,R,l,m);
    if(R>m) tmp2=query(tree[x].r,tree[y].r,L,R,m+1,r);
    return Node(tmp1.sum+tmp2.sum,tmp1.num+tmp2.num);
}
bool judge(int l,int r,double res,int x,int y)
{
    double cut=(sum[r]-sum[l-1]+0.0)*(x*1.0)/(y*1.0);
    Node tmp=query(root[l-1],root[r],ceil(res),maxh,1,maxh);
    if(tmp.sum*1.0-tmp.num*res>=cut) return true;
    return false;
}
int main()
{
    int l,r,x,y;
    maxh=cnt=0;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
        maxh=max(maxh,a[i]);
    }
    build(root[0],1,maxh);
    for(int i=1;i<=n;i++) update(root[i],root[i-1],1,maxh,a[i]);
    while(q--)
    {
        scanf("%d%d%d%d",&l,&r,&x,&y);
        double L=0.0,R=maxh;
        while(R-L>eps)
        {
            double mid=(L+R)/2;
            if(judge(l,r,mid,x,y)) L=mid;
            else R=mid;
        }
        printf("%.15lf\n",L);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值