2014.11.5聪明的质检员

复习冲刺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] wjW,j

这批矿产的检验结果 Y 为各个区间的检验值之和。即:

Y=i=1mYi

若这批矿产的 检验结果与所给标准值 S 相差太多,就需要再去检验另一批矿产。小 T 不想费时间去检验另一批矿产,所以他想通过调整参数 W 的值,让 检验结果尽可能的靠近标准值 S,即使得 SY 的绝对值最小。请你帮忙求出这个最小值。 

一阵高大上的描述后,我死在了求和符号上(不断询问同机房神犇后才大致了解,写代码时也是各种出错。。。),由题意易推,当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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值