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\n",ans);
    }
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章