HDU 6070 (線段樹)(統計顏色)

HDU 6070 Partition

Problem :
給一段長度爲n的序列,要求找出一段區間,使得這段區間的數字種類除以區間長度最小。輸出最後的答案便可。(n <= 60000)(9s時限)
Solution :
顯然,答案是0~1中的一個數字,能夠很天然的想到二分答案的作法。假設目前二分到的答案爲mid,那麼須要判斷
\[\frac{cnt(l, r)}{r- l + 1} <= mid\]
其中cnt(l,r)爲l到r這個區間內的數字種類。變化一下式子能夠獲得:
\[cnt(l, r) + mid * l <= mid * (r + 1)\]
經過枚舉有端點r,使用線段樹維護左邊的式子,每當右端點r向右移動1時,所影響的區間爲r到對應顏色上一次出現的位置,區間總體加1就好了。php

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>

using namespace std;

#define eps 1e-10

const int N = 1e5 + 8;

int a[N], pre[N];
int n;

struct Segment_Tree
{
    double tag[N << 2];
    double lazy[N << 2];
    void pushup(int rt)
    {
        int l = rt << 1, r = rt << 1 | 1;
        tag[rt] = min(tag[l], tag[r]);
    }
    void pushdown(int rt)
    {
        int l = rt << 1, r = rt << 1 | 1;
        if (lazy[rt])
        {
            tag[l] += lazy[rt];
            tag[r] += lazy[rt];
            lazy[l] += lazy[rt];
            lazy[r] += lazy[rt];
            lazy[rt] = 0;
        }
    }
    void build(int l, int r, int rt, double x)
    {
        tag[rt] = lazy[rt] = 0;
        if (l == r)
        {
            tag[rt] = l * x;
            return;
        }
        int m = l + r >> 1;
        build(l, m, rt << 1, x);
        build(m + 1, r, rt << 1 | 1, x);
        pushup(rt);
    }
    void update(int L, int R, int val, int l, int r, int rt)
    {
        if (L <= l && r <= R)
        {
            tag[rt] = tag[rt] + val;
            lazy[rt] += val;
            return;
        }
        pushdown(rt);
        int m = l + r >> 1;
        if (L <= m) update(L, R, val, l, m, rt << 1);
        if (m <  R) update(L, R, val, m + 1, r, rt << 1 | 1);
        pushup(rt);
    }
    double query(int L, int R, int l, int r, int rt)
    {
        if (L <= l && r <= R)
        {
            return tag[rt];
        }
        pushdown(rt);
        int m = l + r >> 1;
        double ans = 1e12;
        if (L <= m) ans = min(ans, query(L, R, l, m, rt << 1));
        if (m <  R) ans = min(ans, query(L, R, m + 1, r, rt << 1 | 1));
        return ans;
    }
}T;

void init()
{
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
}
int sgn(double x)
{
    if (fabs(x) < eps) return 0;
    if (x > 0) return 1;
    return -1;
}

bool check(double mid)
{

    for (int i = 1; i <= n; ++i) pre[i] = 0;
    T.build(1, n, 1, mid);
    for (int i = 1; i <= n; ++i)
    {
        T.update(pre[a[i]] + 1, i, 1, 1, n, 1);
        pre[a[i]] = i;
        if (sgn(mid * (i + 1) - T.query(1, i, 1, n, 1) >= 0)) return 1;
    }
    return 0;
}

void solve()
{
    double l = 0, r = 1;
    while (l + eps < r)
    {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid; else l = mid;
    }
    printf("%.6f\n", l);

}

int main()
{
    cin.sync_with_stdio(0);
    int T; cin >> T;
    for (int cas = 1; cas <= T; ++cas)
    {
        init();
        solve();
    }
}
相關文章
相關標籤/搜索