[NOIP2011 提高组] 聪明的质监员
题目描述
小T
是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有
n
n
n 个矿石,从
1
1
1 到
n
n
n 逐一编号,每个矿石都有自己的重量
w
i
w_i
wi 以及价值
v
i
v_i
vi 。检验矿产的流程是:
1 、给定$ m$ 个区间 [ l i , r i ] [l_i,r_i] [li,ri];
2 、选出一个参数 W W W;
3 、对于一个区间 [ l i , r i ] [l_i,r_i] [li,ri],计算矿石在这个区间上的检验值 y i y_i yi:
y i = ∑ j = l i r i [ w j ≥ W ] × ∑ j = l i r i [ w j ≥ W ] v j y_i=\sum\limits_{j=l_i}^{r_i}[w_j \ge W] \times \sum\limits_{j=l_i}^{r_i}[w_j \ge W]v_j yi=j=li∑ri[wj≥W]×j=li∑ri[wj≥W]vj
其中 j j j 为矿石编号。
这批矿产的检验结果 y y y 为各个区间的检验值之和。即: ∑ i = 1 m y i \sum\limits_{i=1}^m y_i i=1∑myi
若这批矿产的检验结果与所给标准值
s
s
s 相差太多,就需要再去检验另一批矿产。小T
不想费时间去检验另一批矿产,所以他想通过调整参数
W
W
W 的值,让检验结果尽可能的靠近标准值
s
s
s,即使得
∣
s
−
y
∣
|s-y|
∣s−y∣ 最小。请你帮忙求出这个最小值。
输入格式
第一行包含三个整数 n , m , s n,m,s n,m,s,分别表示矿石的个数、区间的个数和标准值。
接下来的 n n n 行,每行两个整数,中间用空格隔开,第 i + 1 i+1 i+1 行表示 i i i 号矿石的重量 w i w_i wi 和价值 v i v_i vi。
接下来的 m m m 行,表示区间,每行两个整数,中间用空格隔开,第 i + n + 1 i+n+1 i+n+1 行表示区间 [ l i , r i ] [l_i,r_i] [li,ri] 的两个端点 l i l_i li 和 r i r_i ri。注意:不同区间可能重合或相互重叠。
输出格式
一个整数,表示所求的最小值。
样例 #1
样例输入 #1
5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
样例输出 #1
10
#include <bits/stdc++.h>
using namespace std;
#define de(x) cout<<x<<" ";
#define Pu puts("");
#define sf(x) scanf("%lld",&x);
typedef long long ll;
const int N=2e5+10,mod=100003;
struct E{
ll w,u;
}s[N];
ll n,m,ans;
ll S;
ll zuo[N],you[N];
ll prew[N],preu[N];
ll fun(int t){
ll res=0;
//ll cnt,sz;
//超时:直接模拟时间复杂度是n^2
// for(int i=1;i<=m;i++){
// cnt=sz=0;
// for(int j=zuo[i];j<=you[i];j++){
// if(s[j].w>=t){
// cnt++;
// sz+=s[j].u;
// }
// }
// res+=sz*cnt;
// }
//使用前缀和:时间复杂度是2*n
memset(prew,0,sizeof(prew));
memset(preu,0,sizeof(preu));
for(int i=1;i<=n;i++){
if(s[i].w>=t){
prew[i]=prew[i-1]+1;
preu[i]=preu[i-1]+s[i].u;
}else{
prew[i]=prew[i-1];
preu[i]=preu[i-1];
}
}
for(int i=1;i<=m;i++){
res+=(prew[you[i]]-prew[zuo[i]-1])*(preu[you[i]]-preu[zuo[i]-1]);
}
return res;
}
int main(){
cin>>n>>m>>S;
ll w_max=0;
for(int i=1;i<=n;i++){
sf(s[i].w)sf(s[i].u)
if(w_max<s[i].w){
w_max=s[i].w;
}
}
for(int i=1;i<=m;i++){
sf(zuo[i])sf(you[i])
}
ll l=1,r=w_max+1,mid;
ll t;
ans=0x3f3f3f3f3f3f3f3f;
while(l<r){
mid=(l+r)/2;
t=fun(mid);
if(t==S) l=mid+1;
else if(t>S) l=mid+1;
else r=mid;
if(abs(t-S)<ans) ans=abs(t-S);
//这里需要记录,因为如果x合适,mid可能会最终会跳动到x+1,x-1(二者没有x优)
}
de(ans)
return 0;
}