bestcoder#86 T1~T3

本文介绍了作者参与bestcoder比赛的经历,详细解析了三道题目:Price List要求在给定商店价格列表中快速查询,T2 NanoApe Loves Sequence涉及数列删减后的最大差值期望,T3则是求解数列中满足条件的区间数量。通过单调队列和预处理等方法解决了这些问题,时间复杂度均为线性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上星期打了人生中第一场bestcoder,死得很惨。
最简单的T1居然被人hack掉了,哎。
T4T5不会做,只放前三题的题解。

T1:Price List
给定 n 家商店的物品价格(每家商店只卖一种商品),其中第i家商店的物品单价为 vi 。有 m 次询问,每次给出一个整数q,问

q>i=1nvi
是否成立。
​​
本题十分简单,只需先求出所有数的和 sum ,如果 q>sum 那么肯定记多了。

时间复杂度为 Θ(n)

但是需要注意的是本题的数据规模为 1n100000 1vi100000 ,那么

i=1nvi
的最大值是 1010 ,超过了int的范围,要用long long。lkb就是因为没有开long long被人hack掉了。

//yhf
#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
    int t; cin>>t;

    for ( ; t>0; t--)
    {
        int n,m; scanf("%d%d",&n,&m);

        long long sum=0;

        for (int i=0; i<n; i++)
        {
            int v; scanf("%d",&v);
            sum+=v;
        }

        for (int i=0; i<m; i++)
        {
            long long s; scanf("%I64d",&s);
            if (s>sum) cout<<1; else cout<<0;
        }

        cout<<endl;
    }

    return 0;
}

T2:NanoApe Loves Sequence
给定一个长度为 n 的数列,任意删去一个数后可得到一个新数列,我们可以计算出所有相邻两数的差的绝对值的最大值。显然这个最大值会随着被删的数改变而改变,假如全部数被删除的概率是相等的话,求差的绝对值的最大值的期望与n的积是多少。

首先,lkb数学不好,期望是个什么鬼?

用准确的数学语言lkb不会描述,不过通过推算样例,猜测这题里面把删去每个数后所有相邻两数的差的绝对值的最大值相加就是解,果然是对的。(不过lkb还是不知道什么是期望,似乎就是每次可能结果的概率乘以其结果的总和?)

这题的做法又是简单的预处理:求出前 i 个数里相邻差值的最大值fi​​ , i n里相邻差值的最大值 gi ​​ ,那么就有

ans=i=1nmax(|Ai1Ai+1|,fi1,gi+1)

问题得解。时间复杂度为 Θ(n)

//yhf
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

#define R(x) scanf("%d",&x)

using namespace std;

const int ML=100000;

int a[ML+10],ml[ML+10],mr[ML+10];

int main()
{
    int t; R(t);

    for ( ; t>0; t--)
    {
        int n; R(n);

        for (int i=1; i<=n; i++) R(a[i]);

        ml[0]=ml[1]=mr[n]=mr[n+1]=0;

        mr[n-1]=abs(a[n]-a[n-1]);

        for (int i=2; i<=n; i++)
         ml[i]=max(ml[i-1],abs(a[i]-a[i-1]));

        for (int i=n-2; i>0; i--)
         mr[i]=max(mr[i+1],abs(a[i]-a[i+1]));

        a[0]=a[2]; a[n+1]=a[n-1];

        long long ans=0;

        for (int i=1; i<=n; i++)
        {
            int nt=abs(a[i+1]-a[i-1]);

            int ma=max(ml[i-1],mr[i+1]);

            // cout<<max(nt,ma)<<endl;

            ans+=max(nt,ma);
        }

        cout<<ans<<endl;
    }

    return 0;
}

T3:给定一个长度为 n 的数列和一个数m,求这个数列中有多少个区间里的 k 大的数不小于m

这题一看区间第 k 大,lkb的第一反应是用各种奇葩数据结构进行维护,但是仔细思考后发现并不需要这么麻烦。

我们可以用单调队列来维护这个区间,显然如果对于某个区间[i,j],满足 f[i..j] 中第 k 大的元素不小于m,那么对于区间 [i,j+1] [i,j+2] ,……, [i,n] 也必然满足区间内第 k 大的数不小于m

于是对于每一个 i ,用单调队列求出最小的j即可。

官方标答如下:

将不小于 m 的数看作1,剩下的数看作0,那么只要区间内1的个数不小于k则可行,枚举左端点,右端点可以通过two-pointer求出。
时间复杂度 Θ(n)

参考程序:

//yhf
#include <algorithm>
#include <iostream>
#include <cstdio>

using namespace std;

int a[200010];

int main()
{
    int t; scanf("%d",&t);

    for ( ; t>0; t--)
    {
        int n,k,m; scanf("%d%d%d",&n,&m,&k);

        for (int i=0; i<n; i++) scanf("%d",&a[i]);

        long long ans=0;

        int sm=0,j=0;

        for (int i=0; i<n; i++)
        {
            for ( ; j<n && sm<k; j++)
             if (a[j]>=m) sm++;

            //cout<<j<<endl;

            if (sm>=k) ans+=n-j+1;

            if (a[i]>=m) sm--;
        }

        cout<<ans<<endl;
    }
}
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值