P1314 聪明的质监员
2019-09-14
我感受到了memset的力量。
题目描述
小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有n个矿石,从1到n 逐一编号,每个矿石都有自己的重量 w_i 以及价值v_i。检验矿产的流程是:
- 给定m个区间[L_i,R_i];
- 选出一个参数W;
- 对于一个区间[L_i,R_i],计算矿石在这个区间上的检验值Y_i:
这批矿产的检验结果Y为各个区间的检验值之和。即:Y_1+Y_2...+Y_m
若这批矿产的检验结果与所给标准值S相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近标准值S,即使得S-Y的绝对值最小。请你帮忙求出这个最小值。
Slove
一 二分
在每个区间内,只有w[]大于W的,才能被选上。
所以W减小,被选入的矿石增多,Y增加;反之Y减小。
条件:
- Y>S,增大W,减小Y,以使|Y-S|减小。
- Y<S,减小W,增大Y,以使|Y-S|减小。
- Y=S,完美。
二 前缀和
使用前缀和以减少区间修改的时间。
- w[]>=W,sum1[i]=sum1[i-1]+1,sum2[i]=sum2[i-1]+v[i],sum1表示公式中前者,sum2表示后者。
- w[]<W,sum1[i]=sum1[i-1],sum2[i]=sum2[i-1]。
Tip
- 答案初始值一定要设的非常大。
- 不开longlong见祖宗。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 200010
#define ll long long
using namespace std;
ll n,m,k;
ll W;
ll ans=999999999999999999,num;
ll l=1,r,mid;
ll s[N],t[N];
ll sum1[N],sum2[N];
ll w[N],v[N];
bool sl(ll cnt){
ll num=0;
memset(sum1,0,sizeof(sum1));
memset(sum2,0,sizeof(sum2));
for(ll i=1;i<=n;i++){
if(w[i]>=cnt) sum1[i]=sum1[i-1]+1,sum2[i]=sum2[i-1]+v[i];
else sum1[i]=sum1[i-1],sum2[i]=sum2[i-1];
}
for(ll i=1;i<=m;i++){
num+=(sum1[t[i]]-sum1[s[i]-1])*(sum2[t[i]]-sum2[s[i]-1]);
}
if(num>k){
ans=min(ans,num-k);
return false;
}
ans=min(ans,k-num);
return true;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
for(ll i=1;i<=n;i++) scanf("%lld%lld",&w[i],&v[i]),r=max(r,w[i]);
for(ll i=1;i<=m;i++) scanf("%lld%lld",&s[i],&t[i]);
r+=2;
while(l<r){
mid=(l+r)/2;
if(sl(mid)) r=mid;
else l=mid+1;
}
printf("%lld",ans);
return 0;
}