HNIT暑期第一次积分赛

题目顺序为个人认为题目难易程度排序

题目:

1.小y的字符串

小y拥有一个由 字符v 和 字符w 组成长度为len的字符串 S,1 ⩽len⩽100.
输出字符串的底有多少个,如图所示有7个

输入格式:

输入一行字符串

输出格式:

输出底的个数

输入样例:

vvwvw

输出样例:

7

解题思路:

(easy)计算v和w的个数,然后把v的个数加上w的个数乘以2即可得到答案。

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin>>s;
    int w=0,v=0;
    for(int i=0;i<s.size();i++)
    {
        if(s[i]=='w')
            w++;
        if(s[i]=='v')
            v++;
    }
    cout<<w*2+v;
    return 0;
}

2.小y看比赛

马上参加这次足球比赛的人一共有n人。
当一名球员犯规时,该球员将得到一张黄牌或一张红牌。
满足以下条件之一的球员将被驱逐。
1.累计两张黄牌。
2.吃到一张红牌。
小y会观看完这场比赛,开始时,球员没有收到任何牌。
游戏中会出现 q 个事件。正确回答事件中提出的问题。
有三种事件,其格式为输入的 "c x",其中 c 是 1 、 2 或 3 。事件如下
1 x:球员 x 吃到黄牌。
2 x:球员 x 吃到一张红牌。
3 x:问你球员 x 是否被除名。回答 "Yes"或 "No"。

  输入格式:

第一行为两个整数n和q.1⩽n,q⩽1×10^{2}
接下来q行每行两个整数c x.
1⩽x⩽n

 输出格式:

打印第三类问题的回答

 输入样例:

在这里给出一组输入。例如:

3 9
3 1
3 2
1 2
2 1
3 1
3 2
1 2
3 2
3 3

 输出样例:

在这里给出相应的输出。例如:

No
No
Yes
No
Yes
No

解题思路:

(easy)直接开两个数组,一个计算红牌数量,一个计算黄牌数量,依据数组下标来存相对应的球员 的判罚情况,遇到第三类问题时判断该球员红牌数量和黄牌数量是否达到驱逐条件。

 代码:

#include<bits/stdc++.h>
using namespace std;
int a[105],b[105];
int main()
{
    int n,q;
    cin>>n>>q;
    for(int i=0;i<q;i++)
    {
        int x,y;
        cin>>x>>y;
        if(x==1)
            a[y]++;
        else if(x==2)
            b[y]++;
        else
        {
            if(a[y]==2||b[y]==1)
                cout<<"Yes"<<endl;
            else
                cout<<"No"<<endl;
        }
    }
    return 0;
}

3.小y的mex

小y有一个n个非负整数,要求选k个数出来组成数组,使得其mex最大。
一个数组的mex值就是最小的,未出现在该数组的非负整数。

输入格式:

第一行为两个整数n和k.1⩽k⩽n⩽3×10^{5}
第二行为n个数 0⩽a_{i}​⩽10^{9}

输出格式:

输出最大mex

输入样例:

7 3
2 0 2 3 2 1 9

输出样例:

3

 解题思路:

(normal)题目中要求的一个数组的mex值就是最小的,未出现在该数组的非负整数,比如0 1 3的mex值就为2,比如1 2 3的mex值就为0,题目要求我们输出最大的mex值,那我们要做的第一件事情就是要对原数组进行排序,因为数再怎么大再怎么连续,你取得数组最起码都要从0开始取,如果0都没有,那不论怎么取,取出来的数组的mex值最大也只能是0,因此先对数组进行排序,然后就可以顺带判断一下第一个数是不是0,如果不是0那就直接输出0结束程序,如果是的话我们就进行下一步的判断,请注意题目要求的是找最大的mex值,因此我个人认为的一个题目关键点就在这里,你在取值的时候,对于连续相同的值,只需要取一个就够了,不要一直无脑往后取,不然你所得的值就不是最大值了,举个例子说明,如果数组是0 1 2 2 3,要取4个数保证mex最大,那就不能取0 1 2 2,这样取得mex只有3,而你如果取0 1 2 3的话,mex的值就可以达到4,此时4就是最大的mex值,所以在遍历的时候,要进行一下判断,可能有点绕,详细判断方法见代码,我已附上注释。

 代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,k;
    cin>>n>>k;
    int a[n];
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    sort(a,a+n);
    if(a[0]!=0)
    {
        cout<<0;
        return 0;
    }
    for(int i=0;i<n-1;i++)
    {
        if(a[i+1]!=a[i])//判断后一个数是否跟前一个数不等
        {
            k--;//不相等的话,这个数我们就取,因此相应要取的数的数量就减去1
            if(a[i+1]!=a[i]+1)//如果要取的数不等于上一个数加1,也就是不连续的话
            {
                cout<<a[i]+1;//我们就直接输出上一个数+1,比如上一个数是4,后一个数到了6,那最大的mex值就是5
                return 0;//直接结束
            }
        }
        if(k==0)//如果所有的数都取完了,并且没有出现上述判断中的不连续的情况
        {
            cout<<a[i]+1;//那就输出当前最后一个被选的数+1
            return 0;
        }
    }
    cout<<a[n-1]+1;/*如果到数组结尾都没选完,并且所有的数都是连续或相等的话,就会触发此语句,所以最大值就为最后一个数+1,
举个例子说明,0 1 2 2 3 3 4,要取6个数,按照上述取法,先取出0 1 2 3 4,只选了5个数,k==0条件不会触发,a[i+1]!=a[i]+1也不会
触发,那么肯定是要去选有重复的数字了,所以最大的mex那就是最后一个数+1,此时选重复数字不影响mex的值的结果,比如0 1 2 2 3 4的
mex值与0 1 2 3 3 4的mex值都是5*/
    return 0;
}

4.小y的礼物

小y在数轴上一共放了n个礼物第i个放在a_{i}处,你要在数线上选择一个长度为 m 的半开区间 [x,x+m),并获得其中包含的所有礼物。
更具体地说,您将按照以下步骤获取礼物。
首先,选择一个实数 x 。
然后获取坐标满足x⩽a_{i}​​<x+m 的所有礼物。
你最多可以获得多少份礼物?

输入格式:

第一行为一个整数n.1⩽n⩽3×10^{5}
第二行为一个整数m.1⩽m⩽10^{9}
第三行为n个整数既a数组 0⩽a_{i}​⩽10^{9}

输出格式:

输出一个整数你能获得的最大礼物

输入样例:

8 6
2 3 5 7 11 13 17 19

输出样例:

4

解题思路:

 (normal)注意看题目中的取值区间是左闭右开,我们可以用到二分函数lower_bound,详细用法见lower_bound和upper_bound,从第一个数开始依次往后找,然后存最大数,最后的答案就是最大数,有一个注意事项就是好像不排序这个题过不了(好像是这样,不太记得我做的时候是不是没排序wrong了一发)。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
    ll  n,m;
    cin>>n>>m;
    ll a[n];
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
    }
    sort(a,a+n);
    ll ans=0;
    for(ll i=0;i<n;i++)
    {
        ans=max(ans,lower_bound(a,a+n,a[i]+m)-a-i);
    }
    cout<<ans;
    return 0;
}

5.小y的线段树

小y有一个长度为n的a数组下标从1开始
一共有3个操作

  • 1 x把数组所有元素变成x
  • 2 c x把数组中第c个元素+x
  • 3 c 输出数组中第c个元素

一定存在3操作

输入格式:

第一行为一个整数n.1⩽n⩽2×10^{5}
第二行为n个整数既a数组 1⩽ai​⩽10^{9}
第一行为一个整数q.1⩽q⩽2×10^{5}
0⩽x⩽10^{9}
接下来q行为q个操作

输出格式:

输出第三种操作

输入样例:

5
3 1 4 1 5
6
3 2
2 3 4
3 3
1 1
2 3 4
3 3

输出样例:

1
8
5

解题思路:

 (common)这个题,按照常规模拟的话,肯定是超时的,那么我们就要寻找突破点,这个题很明显的超时点肯定就是第一个操作,要将所有的数都变为x,普通操作一次下来就是一个O(n),所以我们就要想办法去解决这个操作。首先我用了两个数组a和b,a用来存原始数组里面的数,b来存每次操作的数,最后要输出的时候,根据是否执行操作1来判断是输出原始数组a加b,还是输出执行了1操作后的数再加b,我们可以用一个cnt=-1,来存每次执行1操作时的数,同时,用一个cnt1=0来存执行操作1的次数,再用一个数组c让执行操作2或者3的时候判断该数是不是最新的执行了操作1之后的数,比如执行操作2时,如果没有执行1操作或者当前的数已经是执行了1以后的数,那么就直接加上z,否则的话,先让当前的数变成0再加上z,然后再让该数的c数组的值等于cnt1,这样就代表该数已经是更改后的数,执行操作3的时候,也要进行判断,可能有点绕,详细请见代码。

代码: 

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[1000005],b[1000005],c[1000005];
int main()
{
    ll n;
    scanf("%lld",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%lld",&a[i]);
    }
    ll q;
    scanf("%lld",&q);
    ll cnt=-1;//执行了操作1之后的数
    ll cnt1=0;//执行操作1的次数
    for(int i=0;i<q;i++)
    {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        if(x==1)
        {
            cnt=y;//更新最新的数
            cnt1++;//次数+1
        }
        else if(x==2)
        {
            ll z;
            scanf("%lld",&z);
            if(c[y-1]!=cnt1)//看次数是否能对得上,对不上得话代表不是最新的数,因为我结果是让b[y-1]加上cnt,所以对不上的话直接归零就行了,
            {
                b[y-1]=0;
                c[y-1]=cnt1;
            }
            b[y-1]+=z;
        }
        else if(x==3)
        {
            if(cnt!=-1)//如果执行了操作1
            {
                if(c[y-1]!=cnt1)//如果不是最新数,同上
                {
                    b[y-1]=0;
                    c[y-1]=cnt1;
                }
                printf("%lld\n",b[y-1]+cnt);
            }
            else
                printf("%lld\n",b[y-1]+a[y-1]);
        }
    }
    return 0;
}

6.小y的自助餐厅

小y自助餐厅提供 n 道主菜和 m 道配菜。主菜 i 的价格是 a_{i},配菜 j的价格是 b_{j}​​ 。食堂正在考虑推出新的套餐菜单。套餐包括一道主菜和一道配菜。设 s 为主菜和配菜的价格之和,则套餐的价格为 min⁡(s,p) 。这里,p 是输入中给定的常数。

输入格式:

第一行为三个整数n,m,p
第二行为n个整数既a数组
第三行为m个整数既b数组
1⩽n,m⩽2×10^{5}
1⩽a_{i},b_{j}10^{9}
1⩽p⩽2×10^{8}

输出格式:

输出n*m种套装的总价格

输入样例:

2 2 7
3 5
6 1

输出样例:

24

样例解释:

7+4+7+6=24

解题思路: 

(common)这个题直接算的话要用循环嵌套,肯定是超时的,那么我们就要找准突破点用更好的办法来优化时间复杂度,整体的思想就是二分加前缀和,遍历主菜,利用二分来找适合的配菜,同时计算配菜的前缀和,不明白为什么用前缀和的可以自己写几个样例列式算一下,就能发现其中的原理,详细请见代码。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
    ll n,m,p;
    cin>>n>>m>>p;
    ll a[n],b[m],c[m];
    for(int i=0;i<n;i++)
        cin>>a[i];
    for(int i=0;i<m;i++)
        cin>>b[i];
    sort(b,b+m);//对b数组排序后使用二分,a数组没有排序的必要
    c[0]=b[0];
    for(int i=1;i<m;i++)
        c[i]=b[i]+c[i-1];//另开数组计算前缀和,减少和的计算时间复杂度
    ll sum=0;
    for(int i=0;i<n;i++)
    {
        int cn=lower_bound(b,b+m,p-a[i])-b;//找到主菜加上配菜价格大于p的那个配菜的位置
        sum+=cn*a[i]+(m-cn)*p;//cn个主菜和m-cn个p的和
        if(cn>0)
            sum+=c[cn-1];//再加上cn个配菜的价格的和
    }
    cout<<sum<<endl;
    return 0;
}
  • 26
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值