洛谷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 见祖宗