杂题训练之三

https://www.luogu.org/problem/P1314

这题目很明显地告诉我们是二分

如果你看不出来的话:二分的判断。

可以看到:在W取0时,所有的区间内的矿石都可以选上,

而在W大于最大的质量时,所有的矿石都选不上。

然后简单算一下就发现:

W越大,矿石选的越少,W越小,矿石选的越多。

所以,随着W增大,Y值减小;

所以:二分的判断条件出来了:

当Y>s 时,需要增大WW来减小Y,从而|Y-s|变小;

当Ys时,|Y-s|0;

当Y<s时,需要减小W来增大Y,从而|Y-s|变大;

再就是前缀和(因为题目要求是区间和

因为二分是logn,维护前缀和是On的,所以nlogn是稳稳的过

code by std(码风比较丑,不是我写的,我找了个最清晰的):

#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
const int N=1001000;
template<class T>
void read(T &x)
{
    x=0;
    T f;
    f=1;
    char c;
    c=getchar();
    while((c<'0'||c>'9')&&c!='-')
    {
        c=getchar();
    }
    if(c=='-')
    {
        f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=(x<<3)+(x<<1)+(c^48);
        c=getchar();
    }
    x=x*f;
}
int w[N],v[N],L[N],R[N],n,m;
ll s,sum[N],num[N];
ll check(ll W)
{
    int i;
    ll ans;
    ans=0;
    for(i=1;i<=n;i++)
    {
        if(w[i]<W)
        {
            num[i]=num[i-1];
            sum[i]=sum[i-1];
        }
        else
        {
            sum[i]=sum[i-1]+v[i];
            num[i]=num[i-1]+1;
        }
    }
    for(i=1;i<=m;i++)
    {
        ans+=(sum[R[i]]-sum[L[i]-1])*(num[R[i]]-num[L[i]-1]);
    }
    return ans;
}
inline ll labs(ll a)
{
    return a>0 ? a : -a;
}
int main()
{
    int i;
    ll l,r,mid,ans;
    read(n);
    read(m);
    read(s);
    for(i=1;i<=n;i++)
    {
        read(w[i]);
        read(v[i]);
    }
    for(i=1;i<=m;i++)
    {
        read(L[i]);
        read(R[i]);
    }
    l=0;
    r=1e6;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(check(mid)>s)
        {
            l=mid+1;
        }
        else
        {
            r=mid-1;
        }
    }
    ans=labs(check(l)-s);
    if(ans>labs(check(r)-s))
    {
        ans=labs(check(r)-s);
    }
    printf("%lld",ans);
}
posted @ 2019-10-08 08:12  wzx_believer  阅读(75)  评论(0编辑  收藏  举报