HDU 6070题解(二分+线段树)

题面

传送门
此题的题意不是很清晰,要注意的一点是在区间[L,R]中,默认题目编号最后一次出现的时候是AC的
比如1 2 1 2 3 ,在区间[1,4]中,第3次提交时AC第1题,第4次提交时AC第2题,故比例为2/4=0.5
所以此问题可以转化为
给定一个序列,定义区间[l,r]的值为cnt(l,r)rl+1,(cnt(l,r)为区间中不同元素的个数,求值最大的区间,输出其最大值

分析

1.二分答案

很明显此题用直接枚举区间的方法会超时,必须使用二分来降低时间复杂度
如果二分区间端点,则答案不存在单调性,无法二分
因此二分答案,显然答案可取的范围是[0,1)
设二分中点为mid,由题意cnt(l,r)rl+1mid ([l,r]为所有区间中值最小的区间)
两边同乘化简r-l+1,移项,整理得cnt(l,r)+mid×lmid×(r+1)

2.求不等式左边的值,判断mid是否可取到

对于每次二分,枚举r从1~n,对于每一个r,我们再求右端点为r的所有区间的所有区间的值中的最小值
我们维护一棵线段树,线段数的每个叶子节点l表示左端点为l,右端点为r(当前的枚举值)时的值,再统计区间[1,r]最小值
这样我们就求出了右端点为r的所有区间的值中的最小值
由于r从1~n,线段树中的值也在不断变化,我们怎么更新线段树中的值呢?
每次r+1,需要更新[r,r]这个区间,区间加mid×r,
再更新[x,r]各区间的cnt值,设a上一次出现的位置为last[a],则区间[last[a]+1,r]都需要+1,因为区间内出现了一个新的数a,而对于last[a]之前的位置,由于a已经出现过,cnt值不受影响

时间复杂度分析:
二分时间复杂度为O(log21ϵ),其中 ϵ为精度,在此题中设ϵ=105即可
线段树的建树时间复杂度为O(nlog2n),n次插入与查询时间复杂度为O(nlog2n)
所以总的时间复杂度分析为O(nlog2n×log21ϵ)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define maxn 60005
#define eps 1e-5
#define INF 0x7fffffff
using namespace std;
int a[maxn];
int pos[maxn],last[maxn];
struct node{
    int l;
    int r;
    double mark;
    double v;
    int len(){
        return r-l+1;
    }
}tree[maxn<<2];
void push_up(int pos){
    tree[pos].v=min(tree[pos<<1].v,tree[pos<<1|1].v);
}
void build(int l,int r,int pos){
    tree[pos].l=l;
    tree[pos].r=r;
    tree[pos].v=0;
    tree[pos].mark=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,pos<<1);
    build(mid+1,r,pos<<1|1);
} 
void push_down(int pos){
    if(tree[pos].mark!=0){
        tree[pos<<1].v+=tree[pos].mark;
        tree[pos<<1|1].v+=tree[pos].mark;
        tree[pos<<1].mark+=tree[pos].mark;
        tree[pos<<1|1].mark+=tree[pos].mark;
        tree[pos].mark=0;
    }
}
void update(int L,int R,double v,int pos){
    int l=tree[pos].l,r=tree[pos].r;
    if(L<=l&&R>=r){
        tree[pos].v+=v;
        tree[pos].mark+=v;
        return;
    } 
    push_down(pos); 
    int mid=(l+r)>>1;
    if(L<=mid) update(L,R,v,pos<<1);
    if(R>mid) update(L,R,v,pos<<1|1);
    push_up(pos);
}
double query(int L,int R,int pos){
    int l=tree[pos].l,r=tree[pos].r;
    if(L<=l&&R>=r){
        return tree[pos].v;
    }
    push_down(pos);
    int mid=(l+r)>>1;
    double ans=INF;
    if(L<=mid) ans=min(ans,query(L,R,pos<<1));
    if(R>mid) ans=min(ans,query(L,R,pos<<1|1));
    return ans; 
}
int t,n;
int main(){
    scanf("%d",&t);
    while(t--){
        memset(last,0,sizeof(last));
        memset(pos,0,sizeof(pos));
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++){
            last[i]=pos[a[i]];
            pos[a[i]]=i;
        }
        double l=0,r=1;
        double ans=INF;
        while(fabs(l-r)>eps){
            build(1,n,1);
            double mid=(l+r)/2;
            bool is_min=false;
            for(int i=1;i<=n;i++){
                update(i,i,mid*i,1);
                update(last[i]+1,i,1,1);
                double t=query(1,i,1);
                if(mid*(i+1)>=t){
                    is_min=true;
                    break;
                }
            }
            if(is_min){
                r=mid;
                ans=min(ans,mid);
            } 
            else l=mid;
        }
        printf("%lf
",ans);
    }
} 
原文地址:https://www.cnblogs.com/birchtree/p/9845836.html