题目链接
题意:
给定一个数列 a 长度为 n (n <= 6e5, a1, a2, ..., an <= n),求其子串中 出现的不同数字个数 与 子串的长度 之比的最小值。
这道题的特殊之处在于其是special judge
Output For each test case, print a single line containing a floating number, denoting the lowest ''Dirt Ratio''. The answer must be printed with an absolute error not greater than 10−4 .如果有什么正经(雾)的做法,那么这个值应该是个定值啊,不至于会............
故:二分!
二分后怎么check呢...赛后看题解(。
题解如是说:
二分答案midmid,检验是否存在一个区间满足\frac{size(l,r)}{r-l+1}\leq midr−l+1size(l,r)≤mid,也就是size(l,r)+mid\times l\leq mid\times (r+1)size(l,r)+mid×l≤mid×(r+1)。
从左往右枚举每个位置作为rr,当rr变化为r+1r+1时,对sizesize的影响是一段区间加11,线段树维护区间最小值即可。
再说明白一点,当处理到第 i 个数时,线段树中中每个节点的值的含义为:
1. 每个叶子节点 [L, L] 的值为 [L, i] 一段的 val,
2. 每个非叶子结点 [L, R] 的值为 min{ [l, i].val | L <= l <= R }.
想明白这一点就好写了
Code:
#include <bits/stdc++.h> #define maxn 60010 #define inf 0x3f3f3f3f #define eps 1e-5 #define lson (rt << 1) #define rson (rt << 1 | 1) struct tree { int l, r, tag; double val; }tr[maxn * 4]; double v; int n, pre[maxn], pos[maxn]; inline int midi(int a, int b) { return a + b >> 1; } inline double min(double a, double b) { return a < b ? a : b; } void build(int rt, int l, int r, double x) { tr[rt].l = l; tr[rt].r = r; tr[rt].val = x * l; tr[rt].tag = 0; if (l == r) return; int mid = midi(l, r); build(lson, l, mid, x); build(rson, mid + 1, r, x); } inline void push_up(int rt) { tr[rt].val = min(tr[lson].val, tr[rson].val); } inline void push_down(int rt) { if (tr[rt].tag) { tr[lson].tag += tr[rt].tag; tr[lson].val += tr[rt].tag; tr[rson].tag += tr[rt].tag; tr[rson].val += tr[rt].tag; tr[rt].tag = 0; } } void modify(int rt, int l, int r, int r0) { if (tr[rt].l == l && tr[rt].r == r) { tr[rt].tag += 1; tr[rt].val += 1; return; } push_down(rt); int mid = midi(tr[rt].l, tr[rt].r); if (r <= mid) modify(lson, l, r, r0); else if (l > mid) modify(rson, l, r, r0); else { modify(lson, l, mid, r0); modify(rson, mid + 1, r, r0); } push_up(rt); } void query(int rt, int r) { if (tr[rt].r <= r) { v = min(v, tr[rt].val); return; } if (tr[rt].l == tr[rt].r) return; push_down(rt); int mid = midi(tr[rt].l, tr[rt].r); query(lson, r); if (r > mid) query(rson, r); push_up(rt); } bool check(double x) { // printf("x : %.3f\n", x); build(1, 1, n, x); for (int r = 1; r <= n; ++r) { modify(1, pre[r] + 1, r, r); v = inf; query(1, r); if (v <= (r + 1) * x) { // printf("%d\n", r); return true; } } return false; } void work() { memset(pos, 0, sizeof(pos)); scanf("%d", &n); for (int i = 1; i <= n; ++i) { int x; scanf("%d", &x); pre[i] = pos[x]; pos[x] = i; } double l = 0, r = 1.0, mid; while (r - l > eps) { mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } printf("%.10f\n", mid); } int main() { int T; scanf("%d", &T); while (T--) work(); return 0; }