Luogu P1314 【NOIP2011】聪明的质检员|前缀和+二分

题目链接

题意很明确了。所以直接跳到思路。

Sol 1

最暴力的写法是直接枚举每个参数\(W\)。有分,但显然无法AC。

Sol 2

注意到\(Y\)具有单调性,即随着\(W\)增大而减小。因此可以采用二分求出最靠近标准值\(S\)的两个值。程序段如下

bool ok1(long long x)
{
    long long ans=check(x);//ans求的是Y的值
    if (ans<S) return 0;else return true;//若y过大,则x取小了
}
.....
int main()
{
        long long L=1,R=1000000;
    while (L+1<R)
    {
        long long mid=(L+R)/2;
        if (ok1(mid)) L=mid;else R=mid;
    }//普通的二分
}

利用该方法,时间复杂度会大大减小。

Sol 3

我们的耗时大部分花在了求和,而无论是\(\sum_j v_j\)还是\(\sum_j 1\) 都具有可减性,故我们可以采用前缀和来维护这两项,这样可以用\(O(n)\)修改前缀和,\(O(m)\)查询,时间复杂度是质的飞跃。代码如下

#include<bits/stdc++.h>
using namespace std;
long long n,m,S,vis[300000],s[300000],l[300000],r[300000],v[300000],w[300000];
long long check(long long x)
{
    long long ans=0;
    for (long long i=1;i<=n;i++)
    {
        if (w[i]>=x) vis[i]=vis[i-1]+1,s[i]=s[i-1]+v[i];
        else vis[i]=vis[i-1],s[i]=s[i-1];//判断的同时维护前缀和
    }
    for (long long i=1;i<=m;i++)
    {
        ans+=(vis[r[i]]-vis[l[i]-1])*(s[r[i]]-s[l[i]-1]);//O(1)求答案
    }
    return ans;
}
bool ok1(long long x)
{
    long long ans=check(x);
    if (ans<S) return 0;else return true;//上文已出现过
}
int main()
{
    cin>>n>>m>>S;
    for (long long i=1;i<=n;i++)
    {
        cin>>w[i]>>v[i];
    }
    for (long long i=1;i<=m;i++)
    {
        cin>>l[i]>>r[i];
    }
    long long L=1,R=1000000;
    while (L+1<R)
    {
        long long mid=(L+R)/2;
        if (ok1(mid)) L=mid;else R=mid;
    }//前面已出现过
    cout<<min(check(L)-S,S-check(R))<<endl;//以L为参数的答案大于S,以R为参数的小于S
    return 0;
}

后记

本题还有一道类似的题目P1083 借教室,同为NOIP题,有兴趣的同学可以写写。

猜你喜欢

转载自www.cnblogs.com/fmj123/p/11804774.html
今日推荐