聪明的质检员
聪明的质检员
https://www.luogu.com.cn/problem/P1314
梳理题意:
-
给定矿石的数目n以及每个矿石的重量和价值;还定m个询问区间以及标准值s
-
要求对于每个区间,求出相应的Yi值;再求和算出总的Y值(W的值需要我们自己去枚举)
- 计算公式:
将计算Yi的公式翻译为代码实现,就是:在每一个区间中寻找价值大于W的矿石,找到一个就用tot累加(注意:只是累加个数),再用tott累加价值;最后Yi=tot×tott
- 我们的总任务是:找出一个W,使得对应的Y与s的绝对值最小。最后输出最小的|s-Y|
做题历程:
-
读完题意,就想到暴力枚举:从0-n×m依次枚举W,在计算出对应的|s-Y|值,最后比较出最小输出即可。肯定会超时,但至少10分左右
-
但是交上去是0分。最开始是怀疑枚举的范围小了,就扩大范围枚举,还是0分
-
于是怀疑是将Yi的计算公式打错了。果然,前两次的代码将tot全部用作累加重量了!完全背离了题意。然后改了计算公式,交上去,有25分了,其他点全部超时。代码如下:
#include <bits/stdc++.h>
using namespace std;
long long n,m,s,maxn,sum=0x3f3f3f3f,l[200001],r[200001];
struct node {
long long w,v;
}a[200001];
inline void solve(int x) {
long long y=0;
for(register int i=1;i<=m;i++) {
long long tot=0,tott=0;
for(register int j=l[i];j<=r[i];j++) {
if(a[j].w>=x) {
tot++;
tott+=a[j].v;
}
}
y=y+(tot*tott);
}
sum=min(sum,abs(y-s));
}
int main() {
scanf("%lld%lld%lld",&n,&m,&s);
for(register int i=1;i<=n;i++) {
scanf("%lld%lld",&a[i].w,&a[i].v);
maxn=max(maxn,a[i].w);
}
for(register int i=1;i<=m;i++) {
scanf("%lld%lld",&l[i],&r[i]);
}
for(register int i=0;i<=maxn;i++) {
solve(i);
}
printf("%lld",sum);
return 0;
}
- 既然枚举有分,那就说明二分也能做且二分的得分会更高一些!那就将代码改为二分。二分代码,一般先直接套板子,再根据题意进行修改。交上去,有70分了!其他的还是超时问题。代码如下:
#include <bits/stdc++.h>
using namespace std;
long long n,m,s,ans=1e15,maxn,l[200010],r[200010];
struct node {
long long w,v;
}a[200010];
int main() {
scanf("%lld%lld%lld",&n,&m,&s);
for(register int i=1;i<=n;i++) {
scanf("%lld%lld",&a[i].w,&a[i].v);
maxn=max(maxn,a[i].w);
}
for(register int i=1;i<=m;i++) {
scanf("%lld%lld",&l[i],&r[i]);
}
long long L=0,R=maxn;
while(L<=R) {
long long y=0,mid=(L+R)>>1;
for(register int i=1;i<=m;i++) {
long long tot=0,tott=0;
for(register int j=l[i];j<=r[i];j++) {
if(a[j].w>=mid) {
tot++;
tott+=a[j].v;
}
}
y+=(tot*tott);
}
if((s-y)<0) L=mid+1;
else R=mid-1;
ans=min(ans,abs(s-y));
}
printf("%lld",ans);
return 0;
}
- 那剩下的问题就只有优化了。打开算法标签,发现还有一个是前缀和!那优化肯定就是前缀和了!看题解,只要加入一个前缀和记录每一个mid值对应的所有矿石的前缀和,就可以优化时间复杂度了。那么对应到二分程序中,就是将tot以及tott改为数组,计算Y值时运用差分(前缀和)即可。再交上去,就AC了。代码如下:
#include <bits/stdc++.h>
using namespace std;
long long n,m,s,ans=1e15,maxn,l[200010],r[200010],tot[200010],tott[200010];
struct node {
long long w,v;
}a[200010];
int main() {
scanf("%lld%lld%lld",&n,&m,&s);
for(register int i=1;i<=n;i++) {
scanf("%lld%lld",&a[i].w,&a[i].v);
maxn=max(maxn,a[i].w);
}
for(register int i=1;i<=m;i++) {
scanf("%lld%lld",&l[i],&r[i]);
}
long long L=0,R=maxn;
while(L<=R) {
long long y=0,mid=(L+R)>>1;
for(register int i=1;i<=n;i++) {
if(a[i].w>=mid) {
tot[i]=tot[i-1]+1;
tott[i]=tott[i-1]+a[i].v;
}
else {
tot[i]=tot[i-1];
tott[i]=tott[i-1];
}
}
for(register int i=1;i<=m;i++) {
y+=(tot[r[i]]-tot[l[i]-1])*(tott[r[i]]-tott[l[i]-1]);
}
if((s-y)<0) L=mid+1;
else R=mid-1;
ans=min(ans,abs(s-y));
}
printf("%lld",ans);
return 0;
}
From:Eleven谦