hdu 6070 二分答案+线段树

来源:互联网 发布:算法导论适合初学者吗 编辑:程序博客网 时间:2024/06/06 16:37

题意: 区间价值为    区间元素种类数  /  区间长度    问最小价值的区间是?


思路:直接求解很困难,考虑二分答案判断,注意这题的关键是将二分答案后的不等式进行变换,如官方题解。二分答案 mid,检验是否存在一个区间满足 size(l,r)/(r−l+1) ≤ mid,也就是 size(l, r) + mid × l ≤mid × (r + 1)。

之后的问题就很好解决了,枚举右端点,开一颗线段树来维护到当前节点的区间不同数(类似树状数组求区间不同数的想法来加入一个新点,主要是通过一个数组维护上一次出现的位置)+  mid × l ,这样就可以轻松判断是否存在解了。


代码:

#include<bits/stdc++.h>using namespace std;#define MEM(a,b) memset(a,b,sizeof(a))#define bug puts("bug");inline int ll(int k) {return 2*k;}inline int rr(int k) {return 2*k+1;}inline int mid(int kk1,int kk2) {return (kk1+kk2)>>1;}const int maxn=3e6+10;struct pr {    double sum,lazy;    int left,right;}tr[maxn+10];void pushdown(int k) {    if(tr[k].lazy>1e-9){        tr[ll(k)].sum+=tr[k].lazy;tr[rr(k)].sum+=tr[k].lazy;        tr[ll(k)].lazy+=tr[k].lazy;        tr[rr(k)].lazy+=tr[k].lazy;        tr[k].lazy=0;    }}void build(int k,int s,int t,double MI) {    tr[k].left=s;tr[k].right=t;    if(s==t) {tr[k].sum=t*MI;return;}    build(ll(k),s,mid(s,t),MI);    build(rr(k),mid(s,t)+1,t,MI);    tr[k].sum=min(tr[ll(k)].sum,tr[rr(k)].sum);    tr[k].lazy=0;}void modify(int k,int s,int t,int x) {    int l=tr[k].left,r=tr[k].right;    if(l==s&&r==t) {        tr[k].lazy+=x,tr[k].sum+=x;        return ;    }    pushdown(k);    int mi=mid(l,r);    if(t<=mi) modify(ll(k),s,t,x);    else if(s>mi) modify(rr(k),s,t,x);    else modify(ll(k),s,mi,x),modify(rr(k),mi+1,t,x);    tr[k].sum=min(tr[ll(k)].sum,tr[rr(k)].sum);}double query(int k,int s,int t) {    int l=tr[k].left,r=tr[k].right;    if(l==s&&r==t) return tr[k].sum;    pushdown(k);    int mi=mid(l,r);    double res=1e20;    if (t<=mi) res=query(ll(k),s,t);    else if(s>mi) res=query(rr(k),s,t);    else res=min(query(ll(k),s,mi),query(rr(k),mi+1,t));    return res;}int a[700000],last[700000],t,n;int cal(double x){    build(1,1,n,x);    MEM(last,0);    for(int i=1;i<=n;i++){        modify(1,last[a[i]]+1,i,1);        last[a[i]]=i;        if(query(1,1,i)<=x*(i+1)) return 1;    }    return 0;}int main(){    scanf("%d",&t);    while(t--){        scanf("%d",&n);        for(int i=1;i<=n;i++) scanf("%d",&a[i]);        double L=0,R=1.0,mi;        for(int i=0;i<25;i++){            mi=(L+R)/2;            if(cal(mi)) R=mi;            else L=mi;        }        printf("%.8f\n",R);    }    return 0;}


原创粉丝点击