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

81 篇文章 0 订阅
59 篇文章 0 订阅

Description
给出n次提交的题目编号,对于一个区间,假设该区间中每个题的最后一次提交是AC,之前都是WA,求所有区间中AC数/提交次数最小值
Input
第一行一整数T表示用例组数,每组用例首先输入一整数n表示总提交次数,之后n个整数a[1]~a[n]表示这n次提交的题目编号(1<=T<=15,1<=n<=6e4,1<=a[i]<=n)
Output
输出AC数/提交次数的最小值
Sample Input
1
5
1 2 1 2 3
Sample Output
0.5000000000
Solution
二分答案,设二分值为mid,num(l,r)表示区间[l,r]中的不同编号的题目数(即为AC数),那么只要存在一个区间[l,r]满足num(l,r)/(r-l+1)<=mid那么说明该二分值合法,可以寻求更小的二分值当答案,否则要找更大的二分值去找合法解,对上式转化一下变成num(l,r)+mid*l<=mid*(r+1),当区间右端点由r-1变成r时,所有num(l,r)中只有num(pre(a[r])+1,r)~num(r,r)加一,其他的均不变(pre(a[r])表示a[r]上一次出现的位置,如果a[r]没有出现则pre(a[r])=0),故可以用线段树维护num(l,r)+mid*l的最小值,每次插入的时候对pre(a[r])+1~r做区间加一操作,每次判合法只需将区间1~r中最小值拿出来和mid*(r+1)比较即可,由于精度要求是1e-4,所以二分14次即可,时间复杂度O(14nlogn)
Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
namespace fastIO 
{
    #define BUF_SIZE 100000
    //fread -> read
    bool IOerror=0;
    inline char nc() 
    {
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if(p1==pend) 
        {
            p1=buf;
            pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if(pend==p1) 
            {
                IOerror=1;
                return -1;
            }
        }
        return *p1++;
    }
    inline bool blank(char ch) 
    {
        return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';
    }
    inline void read(int &x) 
    {
        char ch;
        while(blank(ch=nc()));
        if(IOerror)return;
        for(x=ch-'0';(ch=nc())>='0'&&ch<='9';x=x*10+ch-'0');
    }
    #undef BUF_SIZE
};
using namespace fastIO;
const int INF=1e9,maxn=60001;
#define ls (t<<1)
#define rs ((t<<1)|1)
double Min[maxn<<2];
int Lazy[maxn<<2];
void push_up(int t)
{
    Min[t]=min(Min[ls],Min[rs]);
}
void build(int l,int r,int t,double x)
{
    Lazy[t]=0;
    if(l==r)
    {
        Min[t]=x*l;
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,ls,x),build(mid+1,r,rs,x);
    push_up(t);
}
void push_down(int t)
{
    if(Lazy[t])
    {
        Lazy[ls]+=Lazy[t],Min[ls]+=Lazy[t],
        Lazy[rs]+=Lazy[t],Min[rs]+=Lazy[t];
        Lazy[t]=0;
    }
}
void update(int L,int R,int l,int r,int t,int v)
{
    if(L<=l&&r<=R)
    {
        Lazy[t]+=v,Min[t]+=v;
        return ;
    }
    push_down(t);
    int mid=(l+r)/2;
    if(L<=mid)update(L,R,l,mid,ls,v);
    if(R>mid)update(L,R,mid+1,r,rs,v);
    push_up(t); 
}
double query(int L,int R,int l,int r,int t)
{
    if(L<=l&&r<=R)return Min[t];
    push_down(t);
    int mid=(l+r)/2;
    double ans=INF;
    if(L<=mid)ans=min(ans,query(L,R,l,mid,ls));
    if(R>mid)ans=min(ans,query(L,R,mid+1,r,rs));
    return ans;
}
int T,n,a[maxn],pre[maxn],pos[maxn];
bool check(double x)
{
    build(1,n,1,x);
    for(int i=1;i<=n;i++)
    {
        update(pre[i]+1,i,1,n,1,1);
        if(query(1,i,1,n,1)<=x*(i+1))return 1;
    }
    return 0;
}
int main()
{
    read(T);
    while(T--)
    {
        read(n);
        memset(pos,0,sizeof(pos));
        for(int i=1;i<=n;i++)
        {
            read(a[i]);
            pre[i]=pos[a[i]],pos[a[i]]=i;
        }
        double l=0,r=1,mid;
        int t=14;
        while(t--)
        {
            mid=0.5*(l+r);
            if(check(mid))r=mid;
            else l=mid;
        }
        printf("%.5f\n",mid);
    } 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值