hdu-6070(二分+线段树)

---------题目链接,戳这里------------

题目意思:

题目一通描述,弄得我完全懵逼。幸好讨论区,有题目意思。

题目意思为:定义f(l,r) 为区间 [l,r] 的不同元素个数/区间长度。求最小的 f(l,r) 定义域:\(0 < l \leqslant r \leqslant n\)。题目意思,翻译转一下就是这么简单。

思路:

思路就是,官方题解给出的,二分+线段树;我们二分答案,mid。需要判断mid是否满足,假设我们定义 \(size\left ( l,r \right )\)为区间 [l,r] 的不同元素个数。那么就需要mid满足:\(\frac{size\left ( l,r \right )}{r-l+1}\leq mid\),按照题解进行变形式子可以得到:\(size \left ( l,r \right )+ mid \times l \leq mid \times \left ( r+1 \right )\),我们可以对r进行从左到右的枚举。在每一次枚举中r就是一个常数。我们可以用线段树维护区间最小值,维护 \(size \left ( l,r \right )+ mid \times l\) ,每次r+1,需要更新r,r这个区间,区间加mid×r。还有需要给r区间到之前出现a[r]位置的右边这个区间的size都会加1。所以两次更新,每次我们需要查询区间 [1,r] 的区间最小值。先写一个区间维护最小值的插线问线的线段树。进行二分 。over;二分的时候,如果满足条件说明mid还不是最小的所以把上限\(r = mid\),否则下限\(l = mid\)

代码比较搓。还好能够AC (-.-)

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
#include <map>
#include <list>
#include <queue>
#include <deque>
#include <stack>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long LL;
const LL INF = 2e9 + 1e8;

const double eps = 0.0000000001;
void fre()
{
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
}
#define MSET(a, b) memset(a, b, sizeof(a))
const int MOD = 1e9 + 7;
const int maxn = 1e6 + 100;

int a[maxn], pre[maxn], pos[maxn];

struct nyist
{
    double tree[maxn], lazy[maxn];
    void init()
    {
        MSET(tree, 0);
        MSET(lazy, 0);
    }
    void pushdown(int i)
    {
        if (lazy[i] > eps)
        {
            tree[i << 1] += lazy[i], tree[i << 1 | 1] += lazy[i];
            lazy[i << 1] += lazy[i], lazy[i << 1 | 1] += lazy[i];
            lazy[i] = 0;
        }
    }
    void update(int i, int l, int r, int L, int R, double k)
    {
        if (L == l && R == r)
        {
            tree[i] += k;
            lazy[i] += k;
            return;
        }
        pushdown(i);
        int mid = (l + r) >> 1;
        if (R <= mid)
            update(i << 1, l, mid, L, R, k);
        else if (L > mid)
            update(i << 1 | 1, mid + 1, r, L, R, k);
        else
            update(i << 1, l, mid, L, mid, k), update(i << 1 | 1, mid + 1, r, mid + 1, R, k);
        tree[i] = min(tree[i << 1], tree[i << 1 | 1]);
    }
    double query(int i, int l, int r, int L, int R)
    {
        if (L == l && r == R)
            return tree[i];
        pushdown(i);
        int mid=(l+r)>>1;
        if(R<=mid) return query(i<<1,l,mid,L,R);
        else if(L>mid) return query(i<<1|1,mid+1,r,L,R);
        else return min(query(i<<1,l,mid,L,mid),query(i<<1|1,mid+1,r,mid+1,R));
    }
} ac;

int main()
{
    int ncase;
    scanf("%d", &ncase);
    while (ncase--)
    {
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        MSET(pos,0);
        for (int i = 1; i <= n; i++)
        {
            pre[i]=pos[a[i]];
            pos[a[i]]=i;
        }
        double l = 0, r = 1.0,res;
        while (fabs(l-r)>eps)
        {
            ac.init();
            double mid = (l + r) / 2;
            bool flag = false;
            for (int i = 1; i <= n; i++)
            {
                ac.update(1, 1, n, i, i, mid *i);
                ac.update(1,1,n,pre[i]+1,i,1.0);
                double ans=ac.query(1,1,n,1,i);
                if(ans<=mid*(i+1)) 
                {
                    flag=true;
                    break;
                }   
            }
            if(flag) r=mid,res=mid;
            else l=mid;
        }
        printf("%lf\n",res);
    }
    return 0;
}

posted @ 2017-08-04 10:34  Code-dream  阅读(656)  评论(0编辑  收藏  举报