這道題目比賽的時候做了半天,結果基本就是掛機的狀態。事後看了題解也是一臉懵逼的。最後看了標程才知道怎麼做的。看懂之後才發現這個這個題目那麼有趣,真是思維太巧妙了(吐槽一下多校賽題解,只有做出了的人才能看懂。(ㄒoㄒ))。
他是這樣解決的二分枚舉答案,對於答案如果成立那麼一定存在size(l, r)/(r-l+1) < = mid.所以化簡得到size(l, r) + l * mid <= (r + 1) * mid.
如何在快速的解決這個呢?就是要用到線段樹,首先初始化整個樹的時候每個節點保存的是mid * l的值,然後取枚舉右邊界r。對於每個枚舉的r的這種狀態下,線段樹(L, R)中存放的是,左區間在(L, R) 中,右區間爲r的元素種類+l*mid的最小值。當r增加到r+1的時候(在處理的時候會有一個app數組記錄當前元素出現的上一個位置),對於整個線段樹的影響就是當前位置與當前元素出現的上一位置,這個區間加1。
對於二分的時間複雜度爲logn,枚舉右端點爲n,對於每次枚舉都要一次區間修改操作是logn,一次查詢操作 logn.所以一共爲o(n * logn * logn).
代碼如下:
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
#define Max_N (6*10000+100)
double v[4*Max_N];
int addv[4*Max_N];
int n;
int a[Max_N];
double min1 ;
void build(int node, int l, int r, double m) {
v[node] = m * l;
addv[node] = 0;
if (l == r) return;
int m1 = l + (r - l) / 2;
build(node*2, l, m1, m);
build(node * 2 + 1, m1 + 1, r, m);
}
int add1(int node, int x)
{
v[node] += x;
addv[node] += x;
}
void pushdown(int node)
{
if (addv[node]) {
add1(node * 2, addv[node]);
add1(node * 2 + 1, addv[node]);
}
addv[node] = 0;
}
void change(int node, int l, int r, int y1, int y2)
{
if (y1 <= l && r <= y2) {
add1(node, 1);
return;
}
pushdown(node);
int m = l + (r - l) / 2;
if (m >= y1) change(node * 2, l, m, y1, y2);
if (m+1 <= y2) change(node * 2 + 1, m + 1, r, y1, y2);
v[node] = min(v[node*2], v[node*2+1]);
}
double query(int node, int l, int r, int y1, int y2)
{
if (y1 > r || y2 < l) return 100000000;
if (r <= y2 && l >= y1) {
return v[node];
}
pushdown(node);
int m = l + (r - l) / 2;
return min(query(node * 2, l, m, y1, y2), query(node * 2 + 1, m + 1, r, y1, y2));
}
int app[Max_N];
int main()
{
int T;
cin >> T;
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
double l = 0; double r = 1;
int flag = 0;
while (true) {
double mid = l + (r - l) / 2;
build(1, 1, n, mid);
flag = 0;
memset(app, 0, sizeof(app));
//cout << mid << endl;
for (int i = 1; i <= n; i++) {
change(1, 1, n, app[a[i]] + 1, i);
min1 = query(1, 1, n, 1, i);
if (min1 <= mid * (i*1.0 + 1)) {
flag = 1;
break;
}
app[a[i]] = i;
}
if (flag) r = mid;
else l = mid;
if (r - l <= 0.000001) break;
}
printf("%.10lf\n", (l + r) / 2);
}
return 0;
}