洛谷P1314聪明的质检员

题目传送门

大体思路思路:前缀和+二分

前缀和维护 \(w_j>W\) 的两个式子的前缀和,优化复杂度。

二分 \(W\) 的值,注意的是需要分别将左右边界扩展 \(1\) 个单位
因为题目求的是差值的绝对值的最小值,因此在二分过程中需要维护一个 \(minn\)

根据题目信息,可知 \((s-y)-W\) 的图像是一个下降的单调函数,根据此信息进行二分。

注意本题的二分的判断,需要先判断 \(s-y\) 的值的范围,再返回过程中维护的绝对值,每次二分完后更新一下 \(minn\) 即可。

代码

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int n,m;
ll s;
int l[N],r[N],w[N],v[N];
ll sumw[N],sumv[N];
int L,R;
ll minn,ans;
bool check(int W)
{
    memset(sumw,0,sizeof sumw);
    memset(sumv,0,sizeof sumv);
    for(int i=1;i<=n;i++)
        if(w[i]>=W)
        {
            sumw[i]=sumw[i-1]+1;
            sumv[i]=sumv[i-1]+v[i];
        }
        else 
        {
            sumw[i]=sumw[i-1];
            sumv[i]=sumv[i-1];
        }
    ans=s;
    for(int i=1;i<=m;i++)
        ans-=(sumw[r[i]]-sumw[l[i]-1])*(sumv[r[i]]-sumv[l[i]-1]);
    bool flag=(ans<0);
    ans=llabs(ans);
    return flag;
}
int main()
{
    ios::sync_with_stdio(0);
    cin>>n>>m>>s;
    L=0x3f3f3f3f;
    minn=0x3f3f3f3f3f3f3f3f;
    for(int i=1;i<=n;i++)
    {
        cin>>w[i]>>v[i];
        L=min(L,w[i]);
        R=max(R,w[i]);
    }
    for(int i=1;i<=m;i++)cin>>l[i]>>r[i];
    L--,R++;
    while(L<R)
    {//L,R二分的是W的值
        int mid=L+R>>1;
        if(check(mid))L=mid+1;
        else R=mid;
        minn=min(minn,ans);
    }
    cout<<minn<<"\n";//需要输出的是与标准值s的差
    return 0;
}

总结:

注意挖掘题目信息,找出单调函数的近似图像

注意每一次二分之前先清零

不开 long long 见祖宗

posted @ 2023-05-25 16:07  week_end  阅读(19)  评论(0编辑  收藏  举报