题解:本题主要考查二分+前缀和。
简要题意:
n
n
n个矿石,从
1
−
n
1-n
1−n有重量
w
i
w_i
wi以及价值
v
i
v_i
vi。给定区间
[
L
i
,
R
i
]
[L_i,R_i]
[Li,Ri],选出一个参数
W
W
W,检验值
Y
i
=
∑
1
∗
∑
v
j
,
w
j
>
=
W
Y_i= \sum\ 1* \sum\ v_j,w_j>=W
Yi=∑ 1∗∑ vj,wj>=W,且
Y
=
Y
1
+
Y
2
+
.
.
.
.
+
Y
m
Y=Y_1+Y_2+....+Y_m
Y=Y1+Y2+....+Ym。
给标准值
S
S
S,使得
S
−
Y
S−Y
S−Y的绝对值最小,请你帮忙求出这个最小值。
1.二分:我们可以知道当
W
W
W增大时,选的矿石越少,
Y
Y
Y的值减小;当
W
W
W减小时,选的矿石越多,
Y
Y
Y的值增大。所以:
1.当
Y
=
S
Y=S
Y=S时,
Y
−
S
=
=
0
Y-S==0
Y−S==0;
2.当
Y
>
S
Y>S
Y>S时,需要增大
W
W
W来减小
Y
Y
Y;
3.当
Y
<
S
Y<S
Y<S时,需要减小
W
W
W来增大
Y
Y
Y。
2.前缀和:因为这里是两个区间和再相乘,所以用前缀和求值。
区间数量:
t
[
i
]
.
n
u
m
=
t
[
i
−
1
]
.
n
u
m
+
1
t[i].num=t[i-1].num+1
t[i].num=t[i−1].num+1;
区间价值:
t
[
i
]
.
v
=
t
[
i
−
1
]
.
v
+
e
[
i
]
.
v
t[i].v=t[i-1].v+e[i].v
t[i].v=t[i−1].v+e[i].v;
注意:本题数据很大,开long long,ans值要赋很大
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
struct N
{
long long num,v;
}t[233333];
struct E
{
long long w,v;
}e[233333];
long long n,m,S,maxn=-0x3f3f,minn=0x3f3f3f,ans=0x3f3f3f3f3f3f3f,sum;
int l[233333],r[233333];
bool check(int W)
{
memset(t,0,sizeof(t));
long long Y=0;sum=0;
for(int i=1;i<=n;i++)
if(e[i].w>=W)
{
t[i].num=t[i-1].num+1;
t[i].v=t[i-1].v+e[i].v;
}
else
{
t[i].num=t[i-1].num;
t[i].v=t[i-1].v;
}
for(int i=1;i<=m;i++)
Y+=(t[r[i]].v-t[l[i]-1].v)*(t[r[i]].num-t[l[i]-1].num);
sum=abs(S-Y);
if(Y>S)return 1;
else return 0;
}
int main()
{
cin>>n>>m>>S;
for(int i=1;i<=n;i++)
{
cin>>e[i].w>>e[i].v;
maxn=max(maxn,e[i].w);
minn=min(minn,e[i].w);
}
for(int i=1;i<=m;i++)
cin>>l[i]>>r[i];
int left=minn;int right=maxn;
while(left<=right)
{
int mid=left+right>>1;
if(check(mid))left=mid+1;
else right=mid-1;
if(sum<ans)ans=sum;
}
cout<<ans<<endl;
return 0;
}