题目地址:https://www.luogu.org/problemnew/show/P1314
题目描述
小T
是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 nn 个矿石,从 11到nn 逐一编号,每个矿石都有自己的重量 wiwi 以及价值vivi 。检验矿产的流程是:
1 、给定mm个区间[Li,Ri];
2 、选出一个参数W;
3 、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi:
这批矿产的检验结果Y 为各个区间的检验值之和。即:Y1+Y2...+Ym
若这批矿产的检验结果与所给标准值S相差太多,就需要再去检验另一批矿产。小T
不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近标准值S,即使得S−Y的绝对值最小。请你帮忙求出这个最小值。
输入输出格式
输入格式:
第一行包含三个整数n,m,S,分别表示矿石的个数、区间的个数和标准值。
接下来的n行,每行2个整数,中间用空格隔开,第i+1行表示ii号矿石的重量wi和价值vi。
接下来的m行,表示区间,每行2 个整数,中间用空格隔开,第i+n+1行表示区间[Li,Ri]的两个端点Li 和Ri。注意:不同区间可能重合或相互重叠。
输出格式:
一个整数,表示所求的最小值。
输入输出样例
输入样例#1:
5 3 15 1 5 2 5 3 5 4 5 5 5 1 5 2 4 3 3
输出样例#1:
10
解题思路:
看了大佬的题解之后豁然开朗https://www.luogu.org/problemnew/solution/P1314
对W二分,建立符合条件(w[i]>=W)的前缀和数组,psum[]和pnum[],再差分求区间【l,r】内的Y值
自己打了一下代码,有个需要注意的点:最后输出的结果是long long类型的,可能结果会非常大,以往习惯的#define inf 0x3fffffff就不再正确的,最好设成LONG_LONG_MAX(c++11好像并不支持这个方***编译错误),或者1e+19
ac代码:
#include <iostream>
#include <math.h>
#include <algorithm>
#define maxn 200005
#define inf LONG_LONG_MAX
typedef long long ll;
using namespace std;
ll n,m,s,le=inf,ri=-inf,sub;
ll l[maxn],r[maxn],w[maxn],v[maxn];
bool More(ll W)
{
ll y=0,psum[maxn]={0},pnum[maxn]={0};
sub=0;
for(int i=1;i<=n;i++)
{
if(w[i]>=W)
{
psum[i]=psum[i-1]+v[i];//确定符合条件的前缀和数组
pnum[i]=pnum[i-1]+1;
}
else
{
psum[i]=psum[i-1];
pnum[i]=pnum[i-1];
}
}
for(int i=1;i<=m;i++)
{
y+=(psum[r[i]]-psum[l[i]-1])*(pnum[r[i]]-pnum[l[i]-1]);
}
sub=abs(y-s);
if(y>s)
return true;
else return false;
}
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
scanf("%lld %lld %lld",&n,&m,&s);
for(int i=1;i<=n;i++)
{
scanf("%lld %lld",&w[i],&v[i]);
le=min(le,w[i]);
ri=max(ri,w[i]);
}
for(int i=1;i<=m;i++)
scanf("%lld %lld",&l[i],&r[i]);
ll ans=inf;
while(le<=ri)
{
ll mid=(le+ri)/2;
if(More(mid))
le=mid+1;//增大W让y减小|y-s|减小
else ri=mid-1;
ans=min(ans,sub);
}
printf("%lld",ans);
return 0;
}