HDU 6070 Dirt Ratio(二分+线段树)

来源:互联网 发布:python微信公众号开发 编辑:程序博客网 时间:2024/05/29 12:30

题意:给你一个长度为n的数组,让你求size(l, r)/(r-l+1)的最小值。[l, r]是给定数组一个子序列,size(l, r)的值为这个区间的不同数的个数。(n <= 6e4)


官方题解:

二分答案midmid,检验是否存在一个区间满足\frac{size(l,r)}{r-l+1}\leq midrl+1size(l,r)mid,也就是size(l,r)+mid×lmid×(r+1)

从左往右枚举每个位置作为rr,当rr变化为r+1r+1时,对sizesize的影响是一段区间加11,线段树维护区间最小值即可。时间复杂度O(n\log n\log w)O(nlognlogw)

现在要找的是size(l,r)+mid*l的最小值。在维护线段树的时候,一开始先是一颗空树,然后每次加入一个数(从左到右,位置从1~n)就查询一下。

为什么这样呢?试想一下当我插入一个数的时候会对哪些区间造成影响,也就是哪些区间之前是没有当前插入的这个数的。那应该是当前插入的这个

数与上一次插入这个数的时候的位置的这段区间(记住我充计的是区间的不同数的个数),那么就对这段区间+1。然后再查询,查询的是以 i 为右端点

的最小值是多少。为什么只枚举R不枚举L呢?这里可以结合代码看一下,在线段树中当确定了R之后就会从线段树的根节点开始一直往下查找,知道R

不满足了位置,在这个过程中其实是对所有的L已经遍历了的,所以只需要枚举R就行了(挺巧妙的)。因为每添加一个数就对以当前添加的这个数的

位置为R进行一次查询,所以不仅会对所有的区间查询还对后面的没有影响(后面的数还没添加)。因为有精度,所以二分的次数有限。



代码:

#include<bits/stdc++.h>using namespace std;const int maxn = 1e5+5;int pre[maxn], a[maxn], lazy[maxn*4], n;double tree[maxn*4];void push_up(int root){    tree[root] = min(tree[root*2], tree[root*2+1]);}void push_down(int root, int l, int r){    if(lazy[root])    {        int mid = (l+r)/2;        tree[root*2] += lazy[root];        tree[root*2+1] += lazy[root];        lazy[root*2] += lazy[root];        lazy[root*2+1] += lazy[root];        lazy[root] = 0;    }}void build(int root, int l, int r, double val){    lazy[root] = 0;    if(l == r)    {        tree[root] = val*l;        return;    }    int mid = (l+r)/2;    build(root*2, l, mid, val);    build(root*2+1, mid+1, r, val);    push_up(root);}void update(int root, int l, int r, int i, int j, int val){    if(i <= l && j >= r)    {        lazy[root] += val;        tree[root] += val;        return ;    }    push_down(root, l, r);    int mid = (l+r)/2;    if(i <= mid) update(root*2, l, mid, i, j, val);    if(j > mid) update(root*2+1, mid+1, r, i, j, val);    push_up(root);}double query(int root, int l, int r, int i, int j){    if(i <= l && j >= r)        return tree[root];    push_down(root, l, r);    int mid = (l+r)/2;    double ans = 1e9;    if(i <= mid) ans = min(ans, query(root*2, l, mid, i, j));    if(j > mid) ans = min(ans, query(root*2+1, mid+1, r, i, j));    return ans;}int main(void){    int _;    cin >> _;    while(_--)    {        scanf("%d", &n);        for(int i = 1; i <= n; i++)            scanf("%d", &a[i]);        double l = 0.0, r = 1.0;        double ans = 1.0;        for(int i = 0; i <= 20; i++)        {            double mid = (l+r)/2;            build(1, 1, n, mid);            memset(pre, 0, sizeof(pre));            bool ok = 0;            for(int i = 1; i <= n; i++)            {                update(1, 1, n, pre[a[i]]+1, i, 1);                pre[a[i]] = i;                double tmp = query(1, 1, n, 1, i);                if(tmp <= mid*(i+1))                {                    ok = 1;                    break;                }            }            if(ok) r = mid, ans = mid;            else l = mid;        }        printf("%.10f\n", ans);    }    return 0;}


原创粉丝点击