NOIP2011 聪明的质监员 洛谷P1314

题目描述:

小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1到n 逐一编号,每个矿石都有自己的重量 wi 以及价值vi 。检验矿产的流程是:

1 、给定m 个区间[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 行表示 i 号矿石的重量 wi 和价值vi。

接下来的m 行,表示区间,每行2 个整数,中间用空格隔开,第i+n+1 行表示区间[Li,Ri]的两个端点Li 和Ri。注意:不同区间可能重合或相互重叠。

输出格式:

输出文件名为qc.out。

输出只有一行,包含一个整数,表示所求的最小值。

 

最大值要开到1e18啊!!!!!!

调了一辈子,还是代码经验太少了。

题解:
这题可以用二分做。

l,r分别为w的最小值-1和w的最大值+1,

对于每个mid,我们扫一遍矿石,利用前缀和思想,计算出到i为止,w大于mid的矿石的个数和v的和。

再扫一遍每个区间,求出当前检验值,如果检验值>s的话l=mid+1,否则r=mid-1

如果s=检验值的话直接break

代码:

 

//by lzx 
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=200010;
#define inf 1e18
#define ll long long
ll n,m;
ll s;
struct node{
    ll w,v;
}sa[N];
struct node1{
    ll l,r;
}qu[N];
ll l=inf,r=-inf;
ll ans=inf;
ll sumv[N],sum[N];
ll check(ll x)
{
//    memset(sum,0,sizeof(sum));
    //memset(sumv,0,sizeof(sumv));
    sum[0]=0;
    sumv[0]=0;
    //printf("!%lld\n",x);
    for(ll i=1;i<=n;i++)
    {
        if(sa[i].w>=x) sum[i]=sum[i-1]+1,sumv[i]=sumv[i-1]+sa[i].v;
        else sum[i]=sum[i-1],sumv[i]=sumv[i-1];
    }
//    printf("\n");
    ll yu=0;
    for(ll i=1;i<=m;i++)
    {
        yu+=(sum[qu[i].r]-sum[qu[i].l-1])*(sumv[qu[i].r]-sumv[qu[i].l-1]);
    //    printf("%lld %lld\n",sumv[qu[i].r],sumv[qu[i].l-1]);
    }
    ans=min(abs(yu-s),ans);
    //prllf("%lld\n",ans);
    return yu;
}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&s);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld%lld",&sa[i].w,&sa[i].v);
        l=min(sa[i].w,l);r=max(sa[i].w,r);
    }
    for(ll i=1;i<=m;i++) 
    {
        scanf("%lld%lld",&qu[i].l,&qu[i].r);
        if(qu[i].l>qu[i].r) swap(qu[i].l,qu[i].r);
    }
    ll mid;
    l--;
    r++;
    while(l<=r)
    {
        mid=(l+r)>>1;
        
        ll yu=check(mid);
        //printf("%lld %lld %lld %lld\n",l,r,mid,yu);
        if(yu>s)l=mid+1;
        else r=mid-1;
        if(yu==s) {ans=0;break;}
    }
    printf("%lld",ans);
}

 

posted @ 2017-10-21 11:57  LUATS_SOSG  阅读(115)  评论(0)    收藏  举报