• 2017ACM暑期多校联合训练


    题目链接

    Problem Description
    In ACM/ICPC contest, the ''Dirt Ratio'' of a team is calculated in the following way. First let's ignore all the problems the team didn't pass, assume the team passed X problems during the contest, and submitted Y times for these problems, then the ''Dirt Ratio'' is measured as XY. If the ''Dirt Ratio'' of a team is too low, the team tends to cause more penalty, which is not a good performance.

    Picture from MyICPC

    Little Q is a coach, he is now staring at the submission list of a team. You can assume all the problems occurred in the list was solved by the team during the contest. Little Q calculated the team's low ''Dirt Ratio'', felt very angry. He wants to have a talk with them. To make the problem more serious, he wants to choose a continuous subsequence of the list, and then calculate the ''Dirt Ratio'' just based on that subsequence.

    Please write a program to find such subsequence having the lowest ''Dirt Ratio''.

    Input
    The first line of the input contains an integer T(1≤T≤15), denoting the number of test cases.

    In each test case, there is an integer n(1≤n≤60000) in the first line, denoting the length of the submission list.

    In the next line, there are n positive integers a1,a2,...,an(1≤ai≤n), denoting the problem ID of each submission.

    Output
    For each test case, print a single line containing a floating number, denoting the lowest ''Dirt Ratio''. The answer must be printed with an absolute error not greater than 10−4.

    Sample Input
    1
    5
    1 2 1 2 3

    Sample Output
    0.5000000000

    题意:
    在给出的数列里面寻找一段区间使得区间内不同数的个数/区间长度的比值最小,输出这个最小值。

    分析:把可能的结果二分,然后用线段树求解
    如果我们设sum为一个区间内不同数的个数,len为这个区间长度
    我们先二分答案得到k,每次判断这个答案k是否是我们要找的答案。那么我们需要在序列中找一段区间使得它的sum/len<=k转换一下得到sum-lenk<=0,我们每次判断这个区间之内的这个条件是否成立。
    现在问题就很好解决了,sum可以利用线段树解决:从左往右插入数字,设A[i]上一次出现的位置为pre[i],那么[pre[i]+1,i]这一段权值加1,sum[j]表示的是:区间[j,i]内不同数的个数,这样从左往右插入数字后,所有的区间都被枚举过了,那么还剩下len
    k,这个只要每插入一个数A[i],就把[1,i]的权值都减去k即可。

    #include<iostream>
    #include<stdio.h>
    using namespace std;
    #define lchild left,mid,root<<1
    #define rchild mid+1,right,root<<1|1
    const int max_n=6e4+10;
    int n,a[max_n],last[max_n],pre[max_n];///last[i]表示i这个值最后出现的位置,pre[i]表示i这个位置上的数值上次出现的位置
    double sum[max_n << 2], add[max_n << 2];///sum表示的是一个区间之内的和,add起一个中间转换的作用
    void push_down(int root)///向下更新左右子树的节点的值
    {
        sum[root<<1]+=add[root];
        sum[root<<1|1]+=add[root];
        add[root<<1]+=add[root];
        add[root<<1|1]+=add[root];
        add[root]=0;
    }
    
    void push_up(int root)///根据左右子树向上更新根节点的值
    {
        sum[root]=min(sum[root<<1],sum[root<<1|1]);
    }
    
    void build(int left,int right,int root)///建树时每个节点的sum和add都是0(包括最下层的叶子节点)
    {
        sum[root]=0;
        add[root]=0;
        if(left==right)
            return ;
        int mid=(left+right)>>1;
        build(lchild);
        build(rchild);
        push_up(root);
    }
    
    
    void update(int l,int r,double w,int left,int right,int root)
    ///[l,r]是需要更新的区间,[left,right]是每次二分的区间
    {
        if(l<=left&&r>=right)///在整个的区间之内
        {
            add[root]+=w;
            sum[root]+=w;
            return ;
        }
    
        push_down(root);///向下更新
    
        int mid=(left+right)>>1;
        if(l<=mid) update(l,r,w,lchild);///更新左子树
        if(r>mid)  update(l,r,w,rchild);///更新右子树
        push_up(root);///由左右子树向上更新
    }
    
    double query(int l,int r,int left,int right,int root)
    ///[l,r]是需要更新的区间,[left,right]是每次二分的区间
    {
        if(l<=left&&r>=right)  return sum[root];
        push_down(root);
        int mid=(left+right)>>1;
        double ans=n;
        if(l<=mid) ans=min(ans,query(l,r,lchild));
        if(r>mid) ans=min(ans,query(l,r,rchild));
        push_up(root);
        return ans;
    }
    
    bool Find(double m)
    {
        build(1,n,1);
        for(int i=1; i<=n; i++)
        {
            update(pre[i]+1,i,1,1,n,1);
            update(1,i,-m,1,n,1);
            if(query(1,i,1,n,1)<=0) return 1;
        }
        return 0;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            for (int i = 1; i <= n; i++)
                last[i] = pre[i] = 0;
            for(int i=1; i<=n; i++)
            {
                scanf("%d",&a[i]);
                pre[i]=last[a[i]];
                last[a[i]]=i;
            }
            double le=0.0,ri=1.0;
            for(int i=1; i<20; i++)
            {
                double mi=(le+ri)/2;
                if(Find(mi)) ri=mi;
                else
                    le=mi;
            }
            printf("%.9lf",ri);
        }
        return 0;
    }
    
  • 相关阅读:
    C#
    Jquery
    JavaScript
    JavaScript
    JavaScript
    JavaScript
    Html
    JavaScript
    (转)SC Create 创建一个Windows系统服务
    我的MyGeneration
  • 原文地址:https://www.cnblogs.com/cmmdc/p/7284182.html
Copyright © 2020-2023  润新知