复习冲刺Noiping,单手打代码被神犇落下许多题,于是决定集中解决,结果。。。。。。一道题花0.5~1h,我都醉了。。。。。。
noip2011聪敏的质检员 题面:
【问题描述】
小 T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有
n
个矿石,从 1 到
n
逐一编号,每个矿石都有自己的重量
wi
以及价值
vi
。检验矿产的流程是:
1. 给定 m个区间
[Li,Ri]
;
2. 选出一个参数
W
;
3. 对于一个区间
[Li,Ri]
,计算矿石在这个区间上的检验值
Yi
:
Yi=∑j1×∑jvj, j∈[Li,Ri]且 wj≥W,j是矿石编号
这批矿产的检验结果 Y 为各个区间的检验值之和。即:
Y=∑i=1mYi
若这批矿产的 检验结果与所给标准值 S 相差太多,就需要再去检验另一批矿产。小 T 不想费时间去检验另一批矿产,所以他想通过调整参数 W 的值,让 检验结果尽可能的靠近标准值 S,即使得 S−Y 的绝对值最小。请你帮忙求出这个最小值。
一阵高大上的描述后,我死在了求和符号上(不断询问同机房神犇后才大致了解,写代码时也是各种出错。。。),由题意易推,当W越大时,Y越小,所以我们可以对M进行二分查找,当Y>S时向左区间查找,当Y<S时向右区间查找,当无法划分或Y=S时,结束二分,输出min{|Y-S|}最小即可
code:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
struct hp{
long long l,r;
}qes[200001];
struct hq{
long long w,v;
}a[200001];
long long n,m,num[200001],sum[200001];
int main()
{
long long ans,i,j,w1,w2,t,maxn,x,wr2,s;
freopen("qc.in","r",stdin);
freopen("qc.out","w",stdout);
n=m=s=0;
scanf("%lld%lld%lld",&n,&m,&s);
maxn=0;
for (i=1;i<=n;++i)
{a[i].v=a[i].w=0; scanf("%lld%lld",&a[i].w,&a[i].v); maxn=max(maxn,a[i].w);}
for (i=1;i<=m;++i)
{
qes[i].l=qes[i].r=0;
scanf("%lld%lld",&qes[i].l,&qes[i].r);
}
w1=1; wr2=w2=maxn; ans=0x7ffffffffffffff;
while (true)
{
x=0;
memset(sum,0,sizeof(sum));
memset(num,0,sizeof(num));
for (i=1;i<=n;++i)
{
sum[i]=sum[i-1]+(a[i].w>=w2)*a[i].v;
num[i]=num[i-1]+(a[i].w>=w2);
}
for (i=1;i<=m;++i)
x+=(sum[qes[i].r]-sum[qes[i].l-1])*(num[qes[i].r]-num[qes[i].l-1]);
t=abs(x-s);
ans=min(ans,t);
if (x<=s)
{wr2=w2; w2=(w1+w2)/2;}
if ((x==s)||(w1==w2))
break;
if (x>s)
{w1=w2; w2=(w1+wr2)/2; }
}
cout<<ans<<endl;
fclose(stdin);
fclose(stdout);
}