首先二分答案,记答案为
A
n
s
Ans
Ans。
设区间
[
L
,
R
]
[L,R]
[L,R]不同的值的个数为
S
i
z
e
(
L
,
R
)
Size(L,R)
Size(L,R)
则:
A
n
s
<
=
S
i
z
e
(
L
,
R
)
R
−
L
+
1
Ans <= \frac{Size(L,R)}{R-L+1}
Ans<=R−L+1Size(L,R)
移项变形得:
S
i
z
e
(
L
,
R
)
+
L
∗
A
n
s
<
=
A
n
s
(
R
+
1
)
Size(L,R) + L*Ans <= Ans(R+1)
Size(L,R)+L∗Ans<=Ans(R+1)
故可以枚举每个区间的右端点
R
R
R
线段树每个端点维护的是:
A
n
s
∗
i
d
+
S
i
z
e
(
i
d
,
R
)
Ans*id + Size(id,R)
Ans∗id+Size(id,R)
i
d
id
id为每一个端点的下标。
而另外维护一个数组
p
o
s
pos
pos,记录每一个问题编号上一次出现的位置,初始均为
0
0
0
记
R
R
R 位置的问题编号
a
[
R
]
a[R]
a[R]
则线段树区间更新:
[
p
o
s
[
a
[
R
]
]
+
1
,
R
]
[pos[a[R]]+1,R]
[pos[a[R]]+1,R]区间加1。
设二分次数为m,则
O
(
m
)
O(m)
O(m)二分,
O
(
n
)
O(n)
O(n)枚举右端点,
O
(
l
o
g
n
)
O(log n)
O(logn)线段树区间更新和查询。
故总复杂度:
O
(
m
n
l
o
g
n
)
O(mnlog n)
O(mnlogn)
代码:
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define lson rt<<1
#define rson rt<<1|1
const double INF = 1e9 + 7;
const double eps = 1e-8;
const int A = 6e4 + 10;
class Seg_Tree{
public:
int l,r,add;
double Mn;
}Tree[A<<2];
int a[A],pos[A],n;
double MID;
inline void change(int rt,int c){Tree[rt].add+=c,Tree[rt].Mn+=c;}
void push_down(int rt){
if(Tree[rt].add){change(lson,Tree[rt].add);change(rson,Tree[rt].add);Tree[rt].add = 0;}
}
void build_Tree(int rt,int l,int r){
Tree[rt].l = l,Tree[rt].r = r;
Tree[rt].add = 0;
if(l == r){Tree[rt].Mn = MID*l;return;}
int mid = (l+r)>>1;
build_Tree(lson,l,mid);build_Tree(rson,mid+1,r);
Tree[rt].Mn = min(Tree[lson].Mn,Tree[rson].Mn);
}
void update(int rt,int st,int ed,int c){
int l = Tree[rt].l,r = Tree[rt].r;
if(st<=l && r<=ed){
change(rt,c);
return;
}
push_down(rt);
int mid = (l+r)>>1;
if(st<=mid) update(lson,st,ed,c);
if(ed >mid) update(rson,st,ed,c);
Tree[rt].Mn = min(Tree[lson].Mn,Tree[rson].Mn);
}
double query(int rt,int st,int ed){
int l = Tree[rt].l,r = Tree[rt].r;
if(st<=l && r<=ed){
return Tree[rt].Mn;
}
push_down(rt);
int mid = (l+r)>>1;
double res = INF;
if(st<=mid) res = min(res,query(lson,st,ed));
if(ed> mid) res = min(res,query(rson,st,ed));
return res;
}
bool check(){
memset(pos,0,sizeof(pos));
build_Tree(1,1,n);
for(int i=1 ;i<=n ;i++){
update(1,pos[a[i]]+1,i,1);
pos[a[i]] = i;
double Min = query(1,1,i);
if(Min - MID*(i+1) <= 0) return true;
}
return false;
}
void solve(){
scanf("%d",&n);
for(int i=1 ;i<=n ;i++) scanf("%d",&a[i]);
double l = 0,r = 1;
for(int i=1 ;i<=20 ;i++){
MID = (l+r)/2;
if(check()) r = MID;
else l = MID;
}
printf("%.10f\n",r);
}
int main(){
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}