补之前落下的总结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-