• 聪明的质监员(qc)


    【问题描述】

    小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1到 n 逐一编号,每个矿石都有自己的重量 wi以及价值 vi。检验矿产的流程是:

    1、给定 m 个区间[Li,Ri];

    2、选出一个参数 W;

    3、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值 Yi

     

    Yi= ∑1*∑ vjj ∈[Li, Ri] 且 wjW ,j 是矿石编号

        j     j

                                                                                                   m

    这批矿产的检验结果 Y 为各个区间的检验值之和。即: Y = ∑ Yi

                                                                                                  i=1

     

    若这批矿产的检验结果与所给标准值   S   相差太多,就需要再去检验另一批矿产。小    T

    不想费时间去检验另一批矿产,所以他想通过调整参数 W 的值,让检验结果尽可能的靠近

    标准值 S,即使得 S-Y 的绝对值最小。请你帮忙求出这个最小值。

     

    【输入】

    输入文件 qc.in。 

     

    第一行包含三个整数 n,m,S,分别表示矿石的个数、区间的个数和标准值。

    接下来的 n 行,每行 2 个整数,中间用空格隔开,第 i+1 行表示 i 号矿石的重量 wi和价

    值 vi

    接下来的 m 行,表示区间,每行 2 个整数,中间用空格隔开,第 i+n+1 行表示区间[Li,

    Ri]的两个端点 Li和 Ri。注意:不同区间可能重合或相互重叠。

    【输出】

    输出文件名为 qc.out。

    输出只有一行,包含一个整数,表示所求的最小值。

    【输入输出样例】

     

    qc.in

    5 3 15

    1 5

    2 5

    3 5

    4 5

    5 5

    1 5

    2 4

    3 3

     

    【输入输出样例说明】


    qc.out

    10

     

    当 W 选 4 的时候,三个区间上检验值分别为 20、5、0,这批矿产的检验结果为 25,此

    时与标准值 S 相差最小为 10。

    【数据范围】

    对于 10%的数据,有 1≤n,m≤10;

    对于 30%的数据,有 1≤n,m≤500;

    对于 50%的数据,有 1≤n,m≤5,000;

    对于 70%的数据,有 1≤n,m≤10,000-;

    对于 100%的数据,有 1≤n,m≤200,000,0 < wi, vi≤106,0 < S≤1012,1≤Li≤Ri≤n。


     讲解:

    1.10%,这个最暴力的都可以吧

    2.30%,n,m都很小,枚举w=(w1+1,w1,w2+1,w2,w3+1,w3,...,wn+1),算出y之后取最接近的即可,时间复杂度n*m*n

    3.50% 用前缀和优化

    我们要求m次区间和,而被算入其中的都是同一部分,那么就可以在每次算y之前先预处理出前缀和,然后对于m个区间的yi的计算为O(1)

    时间复杂度O(n*m)

    4.70% 二分+线段树优化

    线段树优化其实要比前缀和好想,再加上二分答案

    O(log(max(w))*m*logm)

    5.100% 二分+前缀和优化

     

    代码: //切忌:注意开long long , 包括 pre 前缀 ,ans , t , s;

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    const int MX=200010;
    int w[MX],v[MX],l[MX],r[MX];
    long long pre_n[MX],pre_v[MX];
    int left=0x3f3f3f3f,right,n,m;
    long long s,t,dif;
    
    bool ser(int W)
    {
        t=0;
        memset(pre_n,0,sizeof(pre_n));
        memset(pre_v,0,sizeof(pre_v));
        for(int i=1;i<=n;++i) 
        {
            if(w[i]>=W) {
                pre_n[i]=pre_n[i-1]+1;
                pre_v[i]=pre_v[i-1]+v[i];
            }
            else {
                pre_n[i]=pre_n[i-1];
                pre_v[i]=pre_v[i-1];
            }
        }
        for(int i=1;i<=m;++i) {
            t+=(pre_n[r[i]]-pre_n[l[i]-1]) * (pre_v[r[i]]-pre_v[l[i]-1]);    
        }
        dif=llabs(t-s);
        if(t>s) return true;
        else return false;
    }
    
    int main()
    {
        scanf("%d%d %lld",&n,&m,&s);
        for(int i=1;i<=n;++i) {
            scanf("%d%d",&w[i],&v[i]);
            left=min(left,w[i]);
            right=max(right,w[i]);
        }
        for(int i=1;i<=m;++i) {
            scanf("%d%d",&l[i],&r[i]);
        }
        left-=1,right+=2;
        long long ans=0x3f3f3f3f3f;
        while(left<=right)
        {
            int mid=(left+right)>>1;
            if(ser(mid)) left=mid+1;
            else right=mid-1;
            if(dif<ans) 
                ans=dif;
        }
        printf("%lld",ans);
        return 0;
    }

     

     904 的惨痛,905 的一遍A!

    #include<stdio.h>
    #include<algorithm>
    #define ll long long 
    using namespace std;
    const int MX=200001;
    int n,m,left=0x3f3f3f3f,right=-1,w[MX],v[MX],l[MX],r[MX];
    ll d,s,cs,pre_n[MX],pre_v[MX];
    
    bool ask(int W)
    {
        cs=0;
        for(int i=1;i<=n;++i) 
        {
            pre_n[i]=pre_n[i-1];
            pre_v[i]=pre_v[i-1];
            if(w[i]>=W) {
                pre_n[i]++;
                pre_v[i]+=v[i];
            }
         }
        for(int i=1;i<=m;++i) 
            cs+=(pre_n[r[i]]-pre_n[l[i]-1])*(pre_v[r[i]]-pre_v[l[i]-1]);
        d=llabs(cs-s);
        if(cs>s) return true;
        else return false;
    }
    
    int main()
    {
        scanf("%d%d%lld",&n,&m,&s);
        for(int i=1;i<=n;++i) 
        {
            scanf("%d%d",&w[i],&v[i]);
            left=min(left,w[i]);
            right=max(right,w[i]);
        }
        for(int i=1;i<=m;++i) 
            scanf("%d%d",&l[i],&r[i]);
        ll ans=0x3f3f3f3f3f;
        while(left<=right)
        {
            int mid=(left+right)>>1;
            if(ask(mid)) left=mid+1;
            else right=mid-1;
            if(d<ans) ans=d;
        }
        printf("%lld",ans);
        return 0;
    }
    从0到1很难,但从1到100很容易
  • 相关阅读:
    多项式计算
    递归算法
    递推算法
    穷举算法
    两个数用二进制表示,有多少位不同
    一个整数的二进制数中1的个数
    将十进制数转化为二进制数
    (调用方法)判断一个整数是否为素数两种方法,年份是否是闰年,交换两个数值
    快速排序算法
    用户登录系统
  • 原文地址:https://www.cnblogs.com/qseer/p/9494039.html
Copyright © 2020-2023  润新知