題意給你一個區間,讓你找,區間種類數/區間長度最小是多少。
思路先公式化簡 size/(R - L + 1) <= mid –> size + (L - 1)mid <= R * mid;(mid是二分的值)
對於每一個元素a[i],他能影響的區間是,last[a[i]] + 1 , 到i(last表示這個這個元素上一次出現的位置),他能讓這個區間的種類數+1,由於我們需要快速的找到上面的最小的一坨(size + (L - 1)*mid),所以應該用線段樹來維護這一坨。然後就是線段樹插的問題。(這個問題我想了好久好久,然後這麼回事,每一次,你就想他的L,R都是他的座標,那麼這個L就是當前的i)然後在每個i位置,把上面的一坨插進去。插進去之後,發現他能影響前面的一段區間,就把那個區間整體的值+1(不能把所有的插完再詢問區間,因爲你全部插完之後,可能後面的元素不選,但是他還是影響了前面的元素,所以不能選)。
插完之後,找到最小值,比較一下就可以了(和之前二分的答案比)。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
double setv[N * 4] , minv[N * 4] , v , _min;
int pl , pr , n;
int last[N] , now[N] , A[N];
void pushup(int o , int L , int R)
{
int lc = o * 2 , lr = o * 2 + 1 , M = (L + R) / 2;
minv[o] = 0;
if(L != R){
minv[o] = min(minv[lc] , minv[lr]);
}
minv[o] += setv[o];
}
void update(int o , int L , int R)
{
int lc = o * 2 , lr = o * 2 + 1 , M = (L + R) / 2;
if(pl <= L && pr >= R) setv[o] += v;
else{
if(pl <= M) update(lc , L , M);
if(pr > M) update(lr , M + 1 , R);
}
pushup(o , L , R);
}
void query(int o , int L , int R , double add)
{
int lc = o * 2 , lr = o * 2 + 1 , M = (L + R) / 2;
if(pl <= L && pr >= R) _min = min(_min , minv[o] + add);
else{
if(pl <= M) query(lc , L , M , setv[o]);
if(pr > M) query(lr , M + 1 , R , setv[o]);
}
}
bool check(double ratio)
{
memset(setv , 0 , sizeof(setv));
for(int i = 1 ; i <= n ; i++){
pl = i , pr = i , v = 1 + (i - 1) * ratio;
update(1 , 1 , n);
pl = last[i] + 1;
v = 1;
pr = i - 1;
if(pl <= pr) update(1 , 1 , n);
pl = 1 , pr = i , _min = N;
query(1 , 1 , n , 0);
if(_min <= i * ratio) return 1;
}
return 0;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
memset(last , 0 , sizeof(last));
memset(now , 0 , sizeof(now));
for(int i = 1 ; i <= n ; i++) scanf("%d",&A[i]);
for(int i = 1 ; i <= n ; i++){
last[i] = now[A[i]] , now[A[i]] = i;
}
double l = 0 , r = 1 , m;
for(int i = 1 ; i <= 30 ; i++){
m = (l + r) / 2;
if(check(m)) r = m;
else l = m;
}
printf("%lf\n",r);
}
return 0;
}