每日一题 聪明的质检员

        第一道绿题,第一发就过了70%(QAQ泪目),剩下都超时了,不知道该怎么优化了,看了解析,才知道需要使用前缀和。太妙了,题说给m个区间,n个矿石,要求这m个区间y的值的和,可以直接算从1到n的和,然后使用prev1[r[j]]-prev1[l[j]-1]算出各个区间和。

代码:

#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
int n,m,s;
int w[200010],v[200010],l[200010],r[200010];
//int prev1[200010]={0},prev2[200010]={0};
int y_is(int W)
{
    int h=0,s1=0,s2=0;
    // for(int j=1;j<=m;j++){
    //     s1=0,s2=0;
    // for(int i=l[j];i<=r[j];i++)
    // {
    //     if(w[i]>=W)
    //     {s1+=1;s2+=v[i];}
    // }
    
    // h+=(s1*s2);
    // //cout<<h<<"\n";
    // }
    int prev1[200010]={0},prev2[200010]={0};
        
            for(int j=1;j<=n;j++)
            {
                if(w[j]>=W)
                {prev1[j]=prev1[j-1]+1;
                prev2[j]=prev2[j-1]+v[j];
            }
            else{
                prev1[j]=prev1[j-1];
                prev2[j]=prev2[j-1];
            }
            }
            for(int j=1;j<=m;j++)
            {
                h+=(prev1[r[j]]-prev1[l[j]-1])*(prev2[r[j]]-prev2[l[j]-1]);
            }
    return h;
}
signed main()
{
    
    cin>>n>>m>>s;
   
    for(int i=1;i<=n;i++)
    {
        cin>>w[i]>>v[i];
    }
    for(int i=1;i<=m;i++)
    {
        cin>>l[i]>>r[i];
    }
    int max=w[0],min=w[0];
    for(int i=1;i<=n;i++)
    {
        if(max<w[i])
        max=w[i];
        if(min>w[i])
        min=w[i];
    }
    int i=min-1,j=max+1;
    while(i+1!=j)
    {
        int mid=(i+j)/2;
        if(y_is(mid)<=s)j=mid;
        else i=mid;
    }
    int x=abs(y_is(i)-s),y=abs(y_is(j)-s);
    if(x<y)
    cout<<x;
    else cout<<y;

    //cout<<"aa"<<y_is(4);
    // int *b,*c;
    // b=max_element(w,w+n);
    // c=min_element(w,w+n);
}

要注意的点:

1、发现把前缀和的初始化放在子函数里和子函数外都ac了,发现计算前缀和时,是从prev[0]开始计算的,而prev[0]恒等于0!所以每次计算都会重新赋值,prev初始化在该函数外也不怕叠加,会被覆盖,所以以后计算前缀和要从下标为1开始,下标为0的初值设置为0。

2、再就是二分判断的条件要想明白,如果y小于s,就说明W设置的太大了,需要往左边找(r=mid)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值