HDU_6070_Dirt Ratio_二分_線段樹優化
阿新 • • 發佈:2019-02-03
題目大意:
給一串長度為n的整數數列(1 <= ai <= n),可重複,計算連續子串的 X / Y 的最小值,X為子串中出現不同數字的個數,Y為數列長度。
思路:
在0~1內二分找最小值每次判斷是否存在一個子串的值小於等於mid.
X / Y <= mid
X <= mid * Y
X <= mid * (r - l + 1)
X + mid * (l - 1) <= mid * r
從左到右列舉右界r,當列舉至當前的r時,線段樹中的 0~r 區間內的儲存l到當前r之間出現不同數字個數和mid * (l - 1)的和,用線段樹維護區間最小值與 mid * r 比較.
用pre[]陣列記錄當前數字前一次出現的位置, 在pre[temp] + 1 ~ i 區間 + 1
#include <iostream> #include <cstdio> #include <string.h> #define MAXN 60000 + 10 using namespace std; struct Node { double val; double lazy; } nodes[MAXN << 2]; void build (int l, int r, int root) { Node& n = nodes[root]; n.val = n.lazy = 0; if (l == r) return; int mid = (l + r) >> 1; build(l, mid, root * 2 + 1); build(mid + 1, r, root * 2 + 2); } void push_down(int l, int r, int root) { Node& n = nodes[root]; if (l == r) { n.lazy = 0; return; } else { nodes[root * 2 + 1].val += n.lazy; nodes[root * 2 + 1].lazy += n.lazy; nodes[root * 2 + 2].val += n.lazy; nodes[root * 2 + 2].lazy += n.lazy; n.lazy = 0; } } void update(int ul, int ur, double val, int l, int r, int root) { Node& n = nodes[root]; if (ul <= l && r <= ur) { nodes[root].val += val; nodes[root].lazy += val; return; } int mid = (l + r) >> 1; if (n.lazy > 0) push_down(l, r, root); if (ur <= mid) update(ul, ur, val, l, mid, root * 2 + 1); else if (ul > mid) update(ul, ur, val, mid + 1, r, root * 2 + 2); else { update(ul, ur, val, l, mid, root * 2 + 1); update(ul, ur, val, mid + 1, r, root * 2 + 2); } nodes[root].val = min(nodes[root * 2 + 1].val, nodes[root * 2 + 2].val); } double query(int ql, int qr, int l, int r, int root) { if (ql <= l && r <= qr) return nodes[root].val; int mid = (l + r) >> 1; if (nodes[root].lazy > 0) push_down(l, r, root); if (qr <= mid) return query(ql, qr, l, mid, root * 2 + 1); else if (ql > mid) return query(ql, qr, mid + 1, r, root * 2 + 2); else return min(query(ql, qr, l, mid, root * 2 + 1), query(ql, qr, mid + 1, r, root * 2 + 2)); } int a[MAXN], pre[MAXN]; int main() { int t, n; freopen("in.txt", "r", stdin); scanf("%d", &t); while (t--) { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", a + i); double l = 0, r = 1; while (r - l > 1e-5) { build(0, n - 1, 0); memset(pre, -1, sizeof(pre)); double mid = (r + l) / (double)2; for (int i = 0; i < n; i++) update(i, i, mid * (double)(i - 1), 0, n - 1, 0); int flag = 0; for (int i = 0; i < n; i++) { int temp = a[i]; update(pre[temp] + 1, i, (double)1, 0, n - 1, 0); pre[temp] = i; if (query(0, i, 0, n - 1, 0) <= mid * (double)i) { flag = 1; break; } } if (flag) r = mid; else l = mid; } printf("%f\n", l); } }