題面
傳送門
此題的題意不是很清晰,要注意的一點是在區間[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)爲區間中不同元素的個數,求值最大的區間,輸出其最大值
分析
1.二分答案
很明顯此題用直接枚舉區間的方法會超時,必須使用二分來降低時間複雜度
如果二分區間端點,則答案不存在單調性,無法二分
因此二分答案,顯然答案可取的範圍是[0,1)
設二分中點爲mid,由題意 ([l,r]爲所有區間中值最小的區間)
兩邊同乘化簡r-l+1,移項,整理得
2.求不等式左邊的值,判斷mid是否可取到
對於每次二分,枚舉r從1~n,對於每一個r,我們再求右端點爲r的所有區間的所有區間的值中的最小值
我們維護一棵線段樹,線段數的每個葉子節點l表示左端點爲l,右端點爲r(當前的枚舉值)時的值,再統計區間[1,r]最小值
這樣我們就求出了右端點爲r的所有區間的值中的最小值
由於r從1~n,線段樹中的值也在不斷變化,我們怎麼更新線段樹中的值呢?
每次r+1,需要更新[r,r]這個區間,區間加 ,
再更新[x,r]各區間的cnt值,設a上一次出現的位置爲last[a],則區間[last[a]+1,r]都需要+1,因爲區間內出現了一個新的數a,而對於last[a]之前的位置,由於a已經出現過,cnt值不受影響
時間複雜度分析:
二分時間複雜度爲 ,其中 爲精度,在此題中設 即可
線段樹的建樹時間複雜度爲 ,n次插入與查詢時間複雜度爲
所以總的時間複雜度分析爲
代碼:
#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);
}
}