zoukankan      html  css  js  c++  java
  • [NOIP2011]聪明的质检员

    // 此博文为迁移而来,写于2015年7月14日,不代表本人现在的观点与看法。原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w6ft.html

    1、题目



    2、TAG

           NOIP提高组;二分答案;前缀和。

     

    3、分析
           首先题面一定要看懂,看清(上次我就是没有看懂)。说白了,就是使W取一个最合适的值,使每一个所给区间内满足条件的矿石的数量乘上价值之和,最后计算总和使与S最近。
           我们根据分值分布来分析算法:
           1、30分算法:O(n^3),首先O(n)进行W值选取的枚举,然后O(n^2)判断+计算;
           2、50分算法:O(n^2 log n)。根据每次选取W值我们易发现,所得的总值是一个单调函数,随W增大而减小,且一定存在零点。故为了求得最小值,我们需要得到零点位于那两个整数之间。这样用二分显然比枚举快。O(log n)的二分加上O(n^2)的判断+计算;
           3、100分算法:200000的数据显然只能O(n log n)或者O(n)了,但是这道题O(n)很明显不现实。我们的任务在于,如何消去O(n^2)这么大的判断与计算?这里,可以采取前缀和优化——由于每次计算检验值之和的时候,W值固定,故可以用O(n)的时间扫描一遍用a[i]表示前i个矿石满足条件的个数;b[i]表示前i个满足条件的矿石价值之和。故每次询问一个区间的时候可以O(1)直接相减!这样,复杂度就降到了O(n log n)。(这其实是一个很简单的优化但是我想了很久,看来姿势不够)
           4、(这个可以无视)70分算法:存在这个分数段就必然存在这种算法对吧。。。这一定是给那些用了前缀和却没有用二分的。。。虽然我觉得很不可思议。
           还有一个很重要的数据范围——权值会爆INT。
     
    4、代码
    -----------------------------------------------------------------------------------------------------------------
    #include<cstdio>
    #include<cstring>
    #define MAXN 200005
    #define INF 1ll<<60
    typedef long long ll;
     
    ll max(ll a,ll b) { return (a>b)?a:b; }
    ll min(ll a,ll b) { return (a<b)?a:b; }
    ll abs(ll a) { return (a<0)?-a:a; }
     
    ll n,m,w[MAXN],v[MAXN],l[MAXN],r[MAXN],maxw,nl,nr;
    ll s,ans=INF,s1[MAXN],s2[MAXN];
     
    void init()
    {
            scanf("%lld %lld %lld",&n,&m,&s);
            for (int i=1;i<=n;i++) { scanf("%lld %lld",&w[i],&v[i]); maxw=max(maxw,w[i]); }
            for (int i=1;i<=m;i++) scanf("%lld %lld",&l[i],&r[i]);
    }
     
    ll calc(int now)
    {
            memset(s1,0,sizeof(s1)); memset(s2,0,sizeof(s2));
            ll ret=0;
            for (int i=1;i<=n;i++) if (w[i]>=now) { s1[i]=s1[i-1]+1; s2[i]=s2[i-1]+v[i]; } else { s1[i]=s1[i-1]; s2[i]=s2[i-1]; }
            for (int i=1;i<=m;i++) ret+=(s1[r[i]]-s1[l[i]-1])*(s2[r[i]]-s2[l[i]-1]);
            return ret;
    }
     
    int main()
    {
            init();
            nl=1,nr=maxw;
            while (nl<=nr)
            {
                    int mid=(nl+nr)/2;
                    ll ret=calc(mid);
                    if (ret-s《0) { nr=mid-1; ans=min(ans,s-ret); }
                    else { nl=mid+1; ans=min(ans,ret-s); }
            }
            printf("%lld",ans);
            return 0;
    }
    ---------------------------------------------------------------------------------------------------
  • 相关阅读:
    刷题-力扣-112. 路径总和
    刷题-力扣-1137. 第 N 个泰波那契数
    刷题-力扣-101. 对称二叉树
    刷题-力扣-99. 恢复二叉搜索树
    刷题-力扣-109. 有序链表转换二叉搜索树
    刷题-力扣-108. 将有序数组转换为二叉搜索树
    刷题-力扣-111. 二叉树的最小深度
    刷题-力扣-110. 平衡二叉树
    2019春Python程序设计练习6(0423--0429)
    2019春Python程序设计练习5(0416--0422)
  • 原文地址:https://www.cnblogs.com/jinkun113/p/4683314.html
Copyright © 2011-2022 走看看