CSP模测(一)A卷

补之前落下的总结ing
这次是第一次模测,加上第一题的题面唬人,回想起来当时心里已经不淡定了,特别是第一题没有AC的时候。实际上自己没有冷静下来读题,没有有效的提取题目的信息点。做题很重要的一点就是抽取条件,抽象问题。

(A)坏键监测
题目描述:由于 msy 的朋友都有机械键盘,这让 msy 很是羡慕,今天 msy 的机械键盘终于到了。由于 msy 刚买了一块 TXR3090,所以他只剩了10块钱买键盘,理所当然的是,这个键盘只有26个英文字母和大写锁定的按键。

大写锁定键的功能

    在初始状态下,键入字符显示小写英文字母
    在点击大写锁定后,键盘变为大写锁定状态,键入的字符全部都是大写英文字母
    在键盘为大写锁定状态时,再次点击大写锁定后,大写锁定模式取消,再次键入字母将变为小写字母

msy 想知道他的键盘是否有坏键(存在某个键按下之后无效则说明有坏键),于是他使用脸滚键盘的方法,在一个文档中输入字符,使用这种方法可以保证:

不会有按键被连续的按下,但是一个键可能被不连续的按下多次
所有的键都被按到了
最后一个按到的字符一定是字母。

现在给出 msy 打出的字符串,请你判断他的键盘是否有坏键。
输入描述

本题含有多组数据。
输入的第一行,一个整数 T(1≤T≤20)T(1 \leq T \leq 20)T(1≤T≤20),表示数据的组数。
对于每组数据,输入数据为一行字符串,保证字符串只包含大小写英文字母,且长度不超过 10510^5105。
输出描述

对于每组数据,输出一行。若键盘存在坏键,输出yes,否则输出no。

分析:实际上题目有用的信息只有几行字:
1.不会有按键被连续的按下,但是一个键可能被不连续的按下多次
2.所有的键都被按到了
3.最后一个按到的字符一定是字母
以上几点共同说明了:所有键被按下,则一定包含26个字母。大写键被按下且不会被连续按下,不会被最后一个按下。则只要判断是否含26个字母且含有大写字母即可
(我当时还是脑子极度混乱了,甚至抽取除了什么连续两个字母不会相同的条件,条件加的太多导致只A了六个点)

以下是赛后AC代码:

#include<bits/stdc++.h>
using namespace std;

int main()
{
   int n=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        string line;
        cin>>line;
        int size=line.size();
        if(size<26)//肯定不存在26个字母
        printf("yes\n");
        else
        {
            bool cap[28];//判断是否有大写
            bool small[28];//判断是否有小写
            for(int i=0;i<28;i++)//初始化
            {
                cap[i]=false;
                small[i]=false;
            }

            for(int i=0;i<size;i++)
            {
                if(line[i]>=97)//根据其大小写归入不同的集合
                small[line[i]-97]=true;
                else
                cap[line[i]-65]=true;
            }
            
            bool allsmall=true;
            bool check=false;
            for(int i=0;i<26;i++)
            {
                if(!(cap[i]||small[i]))//某字母无大写也无小写,则字母中有坏键
                {
                    printf("yes\n");
                    check=true;
                    break;
                }
                if(cap[i])//存在大写
                allsmall=false;
            }
            if(!check&&allsmall)
            {
                printf("yes\n");
                check=true;
            }
            if(!check)
            printf("no\n");      
            }
        }
}

(B)神奇的打字机
题目描述:有一个神奇的打字机,首先会打印两个字符 01。之后打印机就会进行复习操作,每次复习操作形容如下:

获取已经打印的内容 S(例如:01);
对 S 进行按位取反,得到 S′(例如:10);
在原有内容后追加 S’(例如:0110);
再执行一次复习操作

开始时:01
在第 1 次执行复习操作后:0110
在第 2 次执行复习操作后:01101001
在第 3次执行复习操作后:0110100110010110
在第 4次执行复习操作后01101001100101101001011001101001
以此类推。

这个打印机会无数次的重复这个过程,可惜纸的大小是有限的,所以只能显示前 n 个字符,你想知道的是,字符串 T在已打印的内容中出现的次数。
输入描述

第一行一个整数 n(2≤n≤106)。
第二行一个字符串 T (1≤∣T∣≤100)(1≤∣T∣≤100),字符串 TTT 中只包含字符 ′0′,′1’。
输出描述

一行一个整数,表示 T在已打印内容中的出现次数。

这道题比较简单,只是有个坑要注意,打印机确定从01开始打印,而不是从输入的字符串开始复习,一开始没搞清楚全WA了,浪费了一些时间

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n=0;
    scanf("%d",&n);
    string t;
    cin>>t;
    string f="01";
    string cp=f;
    int s=t.size();

    while(cp.size()<n)
    {
        int cs=cp.size();
        for(int i=0;i<cs;i++)
        {
            char c=cp[i];
            int j=c-48;
            j=j^1;
            j=j+48;
            cp+=j;
        }
    }

    int i=0;
    int c=cp.size();
    int all=0;
    int check=true;
    while(i+s<=n)
    {
        for(int j=0;j<s;j++)
        {
            if(cp[i+j]!=t[j])
            {
                check=false;
                break;
            }
        }
        if(check)
        {
            all++;
            i+=s;
        }
        else
        {
            i++;
        }
        check=true;
    }
    printf("%d",all);
}

(C)打靶
某一天,小L 又跟 小W 比英语成绩,但是由于 小W 太强了,小L 惨败,于是 小L 想使用自己更擅长的射击与 小W 一较高下。

他们来到了靶场,靶场是一个矩形的空地,在其左下角建立二维直角坐标系,整个靶场处于坐标系的第一象限,第 iii 个靶子的位置可以使用 (xi,yi)进行描述,其中 xi,yi均为正整数,同一个位置至多有一个靶子。

初始时小L在 (0,1) 且只能向 x轴正半轴方向射击,小W在 (1,0)且只能向 y轴正半轴方向射击。两人轮流进行行动,小L先进行操作,行动分为两种:

射击:当正对的方向有靶子时,进行射击操作。每次开枪射出 1 颗子弹,靶子与射出的子弹在相遇后均会立刻消失,此时靶子算作被开枪的人打中。由于 小L 在打靶方面有特殊的技巧,所以 小L 一次射击操作可以开 c 枪,而 小W 只能开 1枪。
移动:当正对的方向没有靶子时,小L 与 小W 会分别向 y 轴正方向与 x轴正方向移动,每次操作可以分别移动最多 dL,dW单位距离,移动会在第一个射击方向有靶子可打的地方立刻停止。

当整个场地中没有可以打的靶子时,比赛立刻结束,他们将以打靶的数量决定输赢。由于两人专心射击,无法统计击中的靶子数量,请你帮助他们统计两人分别击中的靶子的数量。
输入描述

输入的第 1 行包含 4 个数,n,c,dL,dW(1≤n,c≤2×105,1≤dL,dW≤109)分别表示靶子的数量,小L 一次射击操作可以开枪的次数,小L 一次移动操作可移动的最大距离,小W 一次移动操作可移动的最大距离。

接下来的 n 行,每行两个数 xi,yi​ (1≤xi,yi≤1018),表示靶子的位置。
输出描述

输出一行两个整数,以空格隔开,分别表示小L 与 小W 击中的靶子的数量。
样例
输入

8 2 4 1
1 1
6 7
1 7
3 5
6 2
3 6
8 2
4 2

输出

5 3

样例解释

在第一回合中:
    小L先行动:视野中有靶子,进行开枪 1 次,击中靶子 (1,1)。(本行只剩 1 个了,所以无需开 2 枪)
    小W后行动:视野中有靶子,进行开枪 1 次,击中靶子 (1,7)。
在第二回合中:
    小L先行动:射击方向上没有靶子,执行一次移动操作,移动到 (0,2)。(在移动到 (0,2)时发现射击方向上有靶子,所以在 (0,2) 停止本次移动)
    小W后行动:射击方向上已经没有靶子可以打,所以执行一次移动操作,移动到 (2,0)。
在第三回合中:
    小L先行动:视野中有靶子,进行开枪 2 次,击中靶子 (4,2),(6,2)。
    小W后行动:射击方向上没有靶子,执行一次移动操作,移动到 (3,0)。
在第四回合中:
    小L先行动:视野中有靶子,进行开枪 1 次,击中靶子 (8,2)。(本行只剩 1 个了,所以无需开 2 枪)
    小W后行动:视野中有靶子,进行开枪 1 次,击中靶子 (3,5)。
在第五回合中:
    小L先行动:射击方向上没有靶子,执行一次移动操作,移动到 (0,6)。
    小W后行动:视野中有靶子,进行开枪 111 次,击中靶子 (3,6)。
在第六回合中:
    小L先行动:射击方向上没有靶子,执行一次移动操作,移动到 (0,7)。(在移动到 (0,7)时发现射击方向上有靶子,所以在 (0,7)停止本次移动)
    小W后行动:射击方向上没有靶子,执行一次移动操作,移动到 (4,0)。
在第七回合中:
    小L先行动:视野中有靶子,进行开枪 1 次,击中靶子 (6,7)。(本行只剩 1 个了,所以无需开 2 枪)
此时,场地中没有靶子了,比赛结束。

子任务

存在 16% 的测试数据,(1≤xi,yi≤100),且 dL=dW=1
存在 24% 的测试数据,(1≤xi,yi≤5000),且 dL=dW=5000
存在 24%的测试数据,(1≤xi,yi≤109),且dL=dW=109

那一周上的是stl,导致我当时满心想着搞些vector,map什么的,倒搞复杂了,以下代码是后面看学长的代码讲解copy的。

#include<bits/stdc++.h>
using namespace std;

int main()
{
    long long n,c,dl,dw;
    scanf("%lld%lld%lld%lld",&n,&c,&dl,&dw);

    set<pair<long long,long long>> row;//记录行和列上有靶子的位置。为什么用set呢?因为其有序的特性,靶子也是按序击中的
 //所以不要盲目地选择数据结构,而要知道为什么
    set<pair<long long,long long>> col;

    for(int i=0;i<n;i++)
    {
        long long x,y;
        scanf("%lld%lld",&x,&y);
        col.emplace(x,y);
        row.emplace(y,x);
    }

    long long l=1;
    long long w=1;
    int sl=0;
    int sw=0;
    while(!row.empty())
    {
    //解释一下begin,这一步是我实在想不到的,由于set有序性,set的第一个数对应是当前有靶子的最小数,也是打靶人应该站的位置
    //以下是优化,处理两人都离下一个可打靶处非常远的情况
        long long tol=row.begin()->first-l;
        long long tow=col.begin()->first-w;
        if(tol>=dl&&tow>=dw)
        {
            long long step=min(tol/dl,tow/dw);
            l+=step*dl;
            w+=step*dw;
        }

               if(row.begin()->first!=l)
               {
                   l=min(l+dl,row.begin()->first);
               }
               else
               {
                int i=c;
                while(i--&&!row.empty()&&row.begin()->first==l)  
                {
                    col.erase({row.begin()->second,row.begin()->first});
                    row.erase(row.begin());
                    sl++;
                }       
               }
        
            if(col.begin()->first!=w)
            w=min(w+dw,col.begin()->first);
            else
            {
                row.erase({col.begin()->second,col.begin()->first});
                col.erase(col.begin());
                sw++;
            }
    }
    printf("%d %d",sl,sw);
}

总之这次模测体验不咋地,菜的初体验
-the end-

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值