P1314 聪明的质检员 题解
这个题的总体思路————二分,还是不难想的
就是如何处理前缀和比较难想(好吧是我太弱了)
虽然题面给的计算式貌似很复杂的样子
但是其实就是 检验值=区间内满足w[i]>W的矿石个数 * ∑v[i]
因此我们二分判断时,如果检验结果(检验值之和)大于标准值,就说明我们二分出的W还可以再变大,不然就说明W太大了,可以变小试一下,然后记录最小的差值
关于二分停不了导致TLE的问题,我们可以加一个cnt记录二分次数,理论上讲二分50次应该是足够了
下面贴AC代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,v[200010],w[200010];
int l[200010],r[200010];
long long s,cur,sum,ans,pre_v[200010],pre_n[200010];
bool check(int mid)
{
cur=0;sum=0;//因为每次二分出的mid值都不一样
memset(pre_n,0,sizeof(pre_n));//所以留下的矿石也不尽相同
memset(pre_v,0,sizeof(pre_v));//因此要初始化数组和全局变量为0
for(int i=1;i<=n;i++)
{
if(w[i]>=mid)pre_v[i]=pre_v[i-1]+v[i],pre_n[i]=pre_n[i-1]+1;//如果符合条件才记录前缀和
else pre_v[i]=pre_v[i-1],pre_n[i]=pre_n[i-1];
}
for(int i=1;i<=m;i++)
cur+=(pre_n[r[i]]-pre_n[l[i]-1])*(pre_v[r[i]]-pre_v[l[i]-1]);//计算检查值
sum=llabs(cur-s);//llabs()是algorithm库里的函数
return cur>s;//判断cur与s的大小
}
int main()
{
int maxx=-1,minn=0x7fffffff;
scanf("%d%d%lld",&n,&m,&s);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&w[i],&v[i]);
maxx=max(maxx,w[i]);//确定二分上下限
minn=min(minn,w[i]);
}
for(int i=1;i<=m;i++)
scanf("%d%d",&l[i],&r[i]);
int r=maxx,l=minn,mid,cnt=0;
ans=999999999999999;//不要忘了这一步
while(l<=r)
{
cnt++;
if(cnt>50)break;//防止无限循环
mid=(l+r)>>1;
if(check(mid))l=mid+1;
else r=mid-1;
if(sum<ans)ans=sum;
}
printf("%lld",ans);//注意lld
return 0;
}