NOIP2011 聪明的质检员

传送门

题目描述的意思就是,对于给定的所有区间,计算每个区间里面有多少个质量大于w的矿石,把个数乘以价值和即为区间贡献,设定一个w值使得区间贡献和最接近S。

可以看出,这个贡献和肯定是随着w具有单调性变化的,我们又想到二分答案……然后每次更新一下差值最小值,如果贡献和大于S就增大w,否则减小w。

计算的时候使用前缀和既可以O(n)计算出答案,总复杂度O(nlogw)

看一下代码。

// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
typedef long long ll;
const int M = 200005;
const ll INF = 1e17+9;
const ll mod = 1e9+7;

ll read()
{
    ll ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

struct seg
{
    ll l,r;
}b[M];

ll a[M],n,m,S,w[M],v[M],minn = INF,sum[M],vsum[M],L,R;

ll calc(ll x)
{
    ll p = 0;
    memset(sum,0,sizeof(sum));
    memset(vsum,0,sizeof(vsum));
    rep(i,1,n)
    {
    sum[i] = sum[i-1],vsum[i] = vsum[i-1];
    if(w[i] >= x) sum[i]++,vsum[i] += v[i];
    }
    rep(i,1,m) p += (sum[b[i].r] - sum[b[i].l-1]) * (vsum[b[i].r] - vsum[b[i].l-1]);
    return p;
}

int main()
{
    n = read(),m = read(),S = read(),L = 0;
    rep(i,1,n) w[i] = read(),v[i] = read(),R = max(R,w[i]);
    rep(i,1,m) b[i].l = read(),b[i].r = read();
    while(L <= R)
    {
    ll mid = (L + R) >> 1,k = calc(mid);
    minn = min(minn,abs(k-S));
    if(k > S) L = mid + 1;
    else R = mid - 1;
    }
    printf("%lld\n",minn);
    return 0;
}
View Code

 

posted @ 2018-10-23 07:42  CaptainLi  阅读(243)  评论(0编辑  收藏  举报