hdu 6070 Dirt Ratio(线段树+二分)

题意: 给出的题目id表示几次AC,询问区间中最少的通过率

思路:

比赛时,不会枚举区间长度,题解的标程里写的很明白,涨姿势。。

Siz(l,r) 区间内的元素个数/  区间长度    <=二分的答案,就表示该答案可以。

但是元素个数的求法很有意思。

在离线线段树时,可以解决区间内不同元素个数的问题。这次的想法感觉和离线的差不多,都是边更新,边查询。

每一次插入后,都会更新一段。而查找答案时,则是每次都必定会查找所有的前缀的最小值。

注意此时线段树每次建立,维护的是 siz(l,r) + mid * l

#include <algorithm>
#include <stdio.h>
#include <iostream>
using namespace std;
const int maxn=60010;
struct node
{
    int l,r;
    double tag;
    double num;
}tree[maxn*4];
double MID;
int a[maxn];
int has[maxn];
double f;
void push_up(int i)
{
    tree[i].num=min(tree[i<<1].num,tree[i<<1|1].num);
}
void push_down(int i)
{
    if(tree[i].tag)
    {
        tree[i<<1].tag+=tree[i].tag;
        tree[i<<1|1].tag+=tree[i].tag;
        tree[i<<1].num+=tree[i].tag;
        tree[i<<1|1].num+=tree[i].tag;
        tree[i].tag=0;
    }
}
void build(int i,int l,int r)
{
    tree[i].l=l,tree[i].r=r;
    tree[i].num=MID*l;
    tree[i].tag=0;
    if(l==r)
        return ;
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    push_up(i);

}
void update(int i ,int nl,int nr)
{
    if(nl<=tree[i].l&&tree[i].r<=nr)
    {
        tree[i].tag+=1;
        tree[i].num+=1;
        return ;
    }
    push_down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(nl<=mid)
        update(i<<1,nl,nr);
    if(nr>mid)
        update(i<<1|1,nl,nr);
    push_up(i);
}
void ask(int i,int pos)
{
    if(tree[i].r<=pos)
    {
        if(f>tree[i].num)
            f=tree[i].num;
        return ;
    }
    push_down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    ask(i<<1,pos);
    if(pos>mid)
        ask(i<<1|1,pos);
    push_up(i);
}
int main()
{
    int t=0;
    scanf("%d",&t);
    int n;
    for(int cs=1;cs<=t;cs++)
    {
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        double l=0,r=1;
        for(int o=20; o; o--)
        {
            MID=(l+r)/2;
            build(1,1,n);
            for(int i=1; i<=n; i++)
                has[i]=0;
            int j ;
            for(  j=1; j<=n; j++)
            {
                update(1,has[a[j]]+1,j);
                f=0x3f3f3f3f;
                ask(1,j);
                if(f-MID*(j+1)<=0)
                {
                    break;
                }
                has[a[j]]=j;
            }
            if(j<=n)r=MID;
            else l=MID;
        }
        printf("%.10f\n",(l+r)/2);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值