【2019牛客暑期多校训练营(第九场)H Cutting Bamboos】莫队 + bit +二分

本文探讨了使用两个树状数组分别存储竹子的高度和数量,结合二分查找算法来解决特定问题的高效算法实现。通过向上取整和高度超过设定值的竹子总和减去竹子数量乘以设定值的方法,实现了精确的二分检查,解决了队友合作中遇到的难题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

H

知道题意以后离线来做
两个树状数组 一个存高度 一个存个数
那么你二分的是mid
你就要MID = ceil(向上取整)mid, 答案为高度超过这个MID的和 减去有多少个高度超过MID的竹子个数*mid 进行二分check 一开始ans没有 = 0.0 一直WA
后来才知道二分有问题 好在队友的帮助下 解决了此题

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <ctime>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
//#include <random>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;

//#pragma comment(linker,"/STACK:10240000,10240000")
//mt19937 rand_(time(0));
const db EPS = 1e-8;
inline int sign(db a){
    return a< -EPS ? -1 : a > EPS;
}
inline int cmp(db a,db b){
    return sign(a-b);
}
const int MAX_N = 200025;
double ANS[MAX_N];
int block;
int n,m,arr[MAX_N];
namespace bit
{
    long long C[MAX_N<<1];
    long long C_[MAX_N<<1];
    void add(int  x,int v)
    {
        for(;x<MAX_N;x+=x&(-x))
            C[x]+=1LL*v;
    }
    long long get(int x)
    {
        ll res = 0;
        for(;x;x-=x&(-x))
            res+=1LL*C[x];
        return res;
    }
    void ADD(int x,int v)
    {
        for(;x<MAX_N;x+=x&(-x))
            C_[x]+=1LL*v;
    }
    long long GET(int x)
    {
        ll res = 0;
        for(;x;x-=x&(-x))
            res+=1LL*C_[x];
        return res;
    }
}

struct node {
    int l,r,id;
    double x,y;
}q[MAX_N];
bool CMP(node a,node b)
{
    return (a.l/block)^(b.l/block)?a.l<b.l:(((a.l/block)&1)?a.r<b.r:a.r>b.r);
}
void add(int x){
    bit::add(arr[x],arr[x]);
    bit::ADD(arr[x],1);
}
void del(int x){
    bit::add(arr[x],-arr[x]);
    bit::ADD(arr[x],-1);
}

int main(){
    //freopen("1.txt","r",stdin);
    //ios::sync_with_stdio(false);
    scanf("%d%d",&n,&m);
    for(int i = 1;i<=n;++i) scanf("%d",&arr[i]);
    block = n/sqrt(m*2/3);
    for(int i = 1;i<=m;++i)
    {
        scanf("%d%d%lf%lf",&q[i].l,&q[i].r,&q[i].x,&q[i].y);
        q[i].id = i;
    }
    sort(q+1,q+m+1,CMP);
    int L=1,R=0;
    for(int i=1;i<=m;++i){
        int ql=q[i].l,qr=q[i].r;
        int qx = q[i].x;double qy = q[i].y;
        while(R<qr)add(++R);
        while(L>ql)add(--L);
        while(L<ql)del(L++);
        while(R>qr)del(R--);
        double right = 1.0*bit::get(100005)/qy*qx;
        double l = 0.0,r = 100000.0;
        double ans =0.0;
        while(l<=r)
        {
            double mid = 1.0*(l+r)/2;
            int MID = ceil(mid+EPS);
            double left =1.0*bit::get(100005)-bit::get(MID-1)-1.0*(bit::GET(100005)-bit::GET(MID-1))*mid;
            if(cmp(left,right)>=0) {ans = mid;l = mid + EPS;}
            else  r=  mid - EPS;
        }
        ANS[q[i].id] = r;
    }
    for(int i = 1;i<=m;++i) printf("%.15f\n",ANS[i]);
    //cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值