hdu 6070二分+线段树

来源:互联网 发布:软件架构 编辑:程序博客网 时间:2024/05/17 00:14
#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<queue>#include<stack>#include<set>#include<map>#include<cstdlib>#include<cmath>using namespace std;const double inf = 1e9;const int maxn = 60005;const double eps=1e-5;struct SegTreeNode{    double val;    double addMark;//延迟标记}segTree[maxn<<2];//定义线段树int pre[maxn],a[maxn],pos[maxn],n;/*功能:构建线段树root:当前线段树的根节点下标arr: 用来构造线段树的数组istart:数组的起始位置iend:数组的结束位置*/void build(int root, double midd, int istart, int iend){    segTree[root].addMark = 0.0;//----设置标延迟记域    if(istart == iend)//叶子节点        segTree[root].val = istart*midd;    else    {        int mid = (istart + iend) / 2;        build(root*2, midd, istart, mid);//递归构造左子树        build(root*2+1, midd, mid+1, iend);//递归构造右子树        //根据左右子树根节点的值,更新当前根节点的值        segTree[root].val = min(segTree[root*2].val, segTree[root*2+1].val);    }}/*功能:当前节点的标志域向孩子节点传递root: 当前线段树的根节点下标*/void pushDown(int root){    if(segTree[root].addMark > 0)    {        //设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递        //所以是 “+=”        segTree[root*2].addMark += segTree[root].addMark;        segTree[root*2+1].addMark += segTree[root].addMark;        //根据标志域设置孩子节点的值。因为我们是求区间最小值,因此当区间内每个元        //素加上一个值时,区间的最小值也加上这个值        segTree[root*2].val += segTree[root].addMark;        segTree[root*2+1].val += segTree[root].addMark;        //传递后,当前节点标记域清空        segTree[root].addMark = 0.0;    }}/*功能:线段树的区间查询root:当前线段树的根节点下标[nstart, nend]: 当前节点所表示的区间[qstart, qend]: 此次查询的区间*/double query(int root, int nstart, int nend, int qstart, int qend){    //查询区间和当前节点区间没有交集    if(qstart > nend || qend < nstart)        return inf;    //当前节点区间包含在查询区间内    if(qstart <= nstart && qend >= nend)        return segTree[root].val;    //分别从左右子树查询,返回两者查询结果的较小值    pushDown(root); //----延迟标志域向下传递    int mid = (nstart + nend) / 2;    return min(query(root*2, nstart, mid, qstart, qend),               query(root*2+1, mid + 1, nend, qstart, qend));}/*功能:更新线段树中某个区间内叶子节点的值root:当前线段树的根节点下标[nstart, nend]: 当前节点所表示的区间[ustart, uend]: 待更新的区间addVal: 更新的值(原来的值加上addVal)*/void update(int root, int nstart, int nend, int ustart, int uend, int addVal){    //更新区间和当前节点区间没有交集    if(ustart > nend || uend < nstart)        return ;    //当前节点区间包含在更新区间内    if(ustart <= nstart && uend >= nend)    {        segTree[root].addMark += addVal;        segTree[root].val += addVal;        return ;    }    pushDown(root); //延迟标记向下传递    //更新左右孩子节点    int mid = (nstart + nend) / 2;    update(root*2, nstart, mid, ustart, uend, addVal);    update(root*2+1, mid+1, nend, ustart, uend, addVal);    //根据左右子树的值回溯更新当前节点的值    segTree[root].val = min(segTree[root*2].val, segTree[root*2+1].val);}bool check(double mid){    build(1,mid,1,n);    for(int i=1;i<=n;i++)    {        update(1,1,n,pos[i]+1,i,1.0);        if(query(1,1,n,1,i)<=mid*(i+1.0))            return true;    }    return false;}int main(){    int t,i;    scanf("%d",&t);    while(t--&&scanf("%d",&n)!=EOF)    {        memset(pre,0,sizeof(pre));        for(i=1;i<=n;i++)        {            scanf("%d",a+i);            pos[i]=pre[a[i]];            pre[a[i]]=i;        }        double mid,l=0,r=1.0;        while(r-l>eps)        {            mid=(r+l)/2;            if(check(mid))                r=mid;            else                l=mid;        }        printf("%.10f\n",mid);    }    return 0;}

原创粉丝点击