描述
题解
十分巧妙的题,只是好考验英语水平啊……
给定一个序列,求所有区间中的 不同数字的个数 / 区间长度
的最小值。
这里用得是二分答案,线段树维护区间最小即可。
官方题解:
代码
#include <cstdio>
#include <iostream>
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;
const int MAXN = 6e4 + 10;
const int MAXM = MAXN << 2;
const int INF = 0x3f3f3f3f;
const double EPS = 1e-8;
int n;
double tmp;
int a[MAXN];
int ap[MAXN];
int tag[MAXM];
double val[MAXM];
inline void pushdown(int rt)
{
if (tag[rt])
{
tag[lson] += tag[rt];
tag[rson] += tag[rt];
val[lson] += tag[rt];
val[rson] += tag[rt];
tag[rt] = 0;
}
}
void build(int rt, int l, int r, double w)
{
val[rt] = w * l;
tag[rt] = 0;
if (l == r)
{
return ;
}
int m = (l + r) >> 1;
build(lson, l, m, w);
build(rson, m + 1, r, w);
}
void update(int rt, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr)
{
tag[rt] += 1;
val[rt] += 1;
return ;
}
pushdown(rt);
int m = (l + r) >> 1;
if (ql <= m)
{
update(lson, l, m, ql, qr);
}
if (qr > m)
{
update(rson, m + 1, r, ql, qr);
}
val[rt] = min(val[lson], val[rson]);
}
void query(int rt, int l, int r, int p)
{
if (r <= p)
{
if (tmp > val[rt])
{
tmp = val[rt];
}
return ;
}
pushdown(rt);
int m = (l + r) >> 1;
query(lson, l, m, p);
if (p > m)
{
query(rson, m + 1, r, p);
}
}
template <class T>
inline void scan_d(T &ret)
{
char c;
ret = 0;
while ((c = getchar()) < '0' || c > '9');
while (c >= '0' && c <= '9')
{
ret = ret * 10 + (c - '0'), c = getchar();
}
}
int main()
{
// freopen("/Users/zyj/Desktop/input.txt", "r", stdin);
int T;
scan_d(T);
while (T--)
{
scan_d(n);
for (int i = 1; i <= n; i++)
{
scan_d(a[i]);
}
double l = 0, r = 1, m = -1;
while (r - l > EPS)
{
m = (l + r) / 2;
build(1, 1, n, m);
int j;
for (j = 1; j <= n; j++)
{
ap[j] = 0;
}
for (j = 1; j <= n; j++)
{
update(1, 1, n, ap[a[j]] + 1, j);
tmp = INF;
query(1, 1, n, j);
if (tmp - m * (j + 1) <= 0)
{
break;
}
ap[a[j]] = j;
}
if (j <= n)
{
r = m;
}
else
{
l = m;
}
}
printf("%.10f\n", m);
}
return 0;
}