2023秋校赛
lnu2023秋选拔赛
这场没坐大牢,打的还是很舒服的。
大吉好好好!

A: 回文日期
题目描述
在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。
小Y习惯用 \(8\) 位数字表示一个日期,其中,前 \(4\) 位代表年份,接下来 \(2\) 位代表月份,最后 \(2\) 位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。
小Y认为,一个日期是回文的,当且仅当表示这个日期的 \(8\) 位数字是回文的。现在,小Y想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存在的日期是回文的。
一个 \(8\) 位数字是回文的,当且仅当对于所有的 \(i\)(\(1 \le i \le 8\))从左向右数的第 \(i\) 个数字和第 \(9-i\) 个数字(即从右向左数的第 \(i\) 个数字)是相同的。
例如:
- 对于 2016 年 11 月 19 日,用 \(8\) 位数字 \(20161119\) 表示,它不是回文的。
- 对于 2010 年 1 月 2 日,用 \(8\) 位数字 \(20100102\) 表示,它是回文的。
- 对于 2010 年 10 月 2 日,用 \(8\) 位数字 \(20101002\) 表示,它不是回文的。
每一年中都有 \(12\) 个月份:
其中,\(1, 3, 5, 7, 8, 10, 12\) 月每个月有 \(31\) 天;\(4, 6, 9, 11\) 月每个月有 \(30\) 天;而对于 \(2\) 月,闰年时有 \(29\) 天,平年时有 \(28\) 天。
一个年份是闰年当且仅当它满足下列两种情况其中的一种:
- 这个年份是 \(4\) 的整数倍,但不是 \(100\) 的整数倍;
- 这个年份是 \(400\) 的整数倍。
例如:
- 以下几个年份都是闰年:\(2000, 2012, 2016\)。
- 以下几个年份是平年:\(1900, 2011, 2014\)。
输入格式
两行,每行包括一个 \(8\) 位数字。
第一行表示小Y指定的起始日期。
第二行表示小Y指定的终止日期。
保证 \(\mathit{date}_1\) 和 \(\mathit{date}_2\) 都是真实存在的日期,且年份部分一定为 \(4\) 位数字,且首位数字不为 \(0\)。
保证 \(\mathit{date}_1\) 一定不晚于 \(\mathit{date}_2\)。
输出格式
一个整数,表示在 \(\mathit{date}_1\) 和 \(\mathit{date}_2\) 之间,有多少个日期是回文的。
样例 #1
样例输入 #1
20110101
20111231
样例输出 #1
1
样例 #2
样例输入 #2
20000101
20101231
样例输出 #2
2
提示
【样例说明】
对于样例 1,符合条件的日期是 \(20111102\)。
对于样例 2,符合条件的日期是 \(20011002\) 和 \(20100102\)。
思路
这题没啥好说的,就是麻烦,有耐心加思路清晰就行了。
AC代码
#include <bits/stdc++.h>
using namespace std ;
const int max_N=1e5+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int isrun(int n){
if(n%4==0&&n%100!=0||n%400==0){
return 1;
}else{
return 0;
}
}
int main(){
int a,b;
cin>>a>>b;
int y=a/10000;
int m=a/100%100;
int d=a%100;
int y1=b/10000;
int m1=b/100%100;
int d1=b%100;
int ans=0;
while(1){
if(y==y1&&m==m1&&d==d1){
break;
}
int flag=1;
for(int i=1;i<=1;i++){
if(d%10!=y/1000){
flag=0;
break;
}
if(d/10!=y/100%10){
flag=0;
break;
}
if(m%10!=y/10%10){
flag=0;
break;
}
if(m/10!=y%10){
flag=0;
break;
}
}
if(flag==1){
ans++;
}
if(m!=2){
if(m==1||m==3||m==5||m==7||m==8||m==10||m==12){
if(d<31){
d++;
}else{
d=1;
m++;
if(m==13){
m=1;
y++;
}
}
}else{
if(d<30){
d++;
}else{
d=1;
m++;
if(m==13){
m=1;
y++;
}
}
}
}else{
if(isrun(y)==1){
if(d<29){
d++;
}else{
d=1;
m++;
if(m==13){
m=1;
y++;
}
}
}else{
if(d<28){
d++;
}else{
d=1;
m++;
if(m==13){
m=1;
y++;
}
}
}
}
}
cout<<ans<<endl;
return 0;
}
B:幸运数
题目描述
辽宁大学ACM队招新啦!
今年辽宁大学ACM队一共招进了 \(n\) 名同学,小Y在 \(103\) 教室给这 \(n\) 名同学分配了座位。
\(103\) 教室有 \(n\) 个座位排成一排,每个座位坐了一名新同学,每名同学都有一个 \([1, m]\) 范围的幸运数,小Y想知道有多少种情况满足存在两名相邻的同学幸运数相同。
输入格式
输入两个正整数 \(m, n\) 如题所示。
输出格式
一个整数,表示情况数对 \(100003\) 取余的结果。
样例 #1
样例输入 #1
2 3
样例输出 #1
6
提示
【输入输出样例 1 说明】
有 \(6\) 种情况满足存在相邻的同学幸运数相同: \((111),(112),(122),(211),(221),(222)\) 。
【数据范围与约定】
对于$ 100%$ 的数据:\(1 \le m \le 10^8 ,1 \le n \le 10^{12}\) 。
思路
座位分配的所有情况是m^n,题目要求求出存在两名同学相邻且幸运数相同的情况数,这里采用逆向思维,先求出没有任何两名同学相邻且幸运数相同的情况数,若第一位同学有m种情况,第二位要求与第一位不同情况为m-1种,以此类推,结果为m(m-1) (m-1)(m-1)……————m(m-1)^ (n-1),答案为m^n -m*(m-1) ^ (n-1)。这里要注意减法分步取模后加100003后再取一次模避免产生负数答案。
AC代码
#include <bits/stdc++.h>
using namespace std ;
const int max_N=1e5+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
const int M=100003;
//快速幂
ll ksm(ll a,ll b){
ll res=1;
while(b){
if(b&1)
res=res*a%M;
a=a*a%M;
b>>=1;
}
return res;
}
int main(){
int m;
ll n;
cin>>m>>n;
ll num=ksm(m,n);
ll num2=ksm(m-1,n-1)*m%M;
ll ans=(num-num2+M)%M;
cout<<ans<<endl;
return 0;
}
C:逢七过
题目描述
逢七过是一个广为流传的休闲小游戏。参加游戏的每个人要按一定顺序轮流报数,但如果下一个报的数是 \(7\) 的倍数,或十进制表示中含有数字 \(7\),就必须跳过这个数,否则就输掉了游戏。
在一个风和日丽的下午,刚刚结束比赛的小Y和小L闲得无聊玩起了这个报数游戏。但在只有两个人玩的情况下计算起来还是比较容易的,因此他们玩了很久也没分出胜负。此时小Y灵光一闪,决定把这个游戏加强:任何一个十进制中含有数字 \(7\) 的数,它的所有倍数都不能报出来!
形式化地,设 \(p(x)\) 表示 \(x\) 的十进制表示中是否含有数字 \(7\),若含有则 \(p(x) = 1\),否则 \(p(x) = 0\)。则一个正整数 \(x\) 不能被报出,当且仅当存在正整数 \(y\) 和 \(z\) ,使得 \(x = yz\) 且 \(p(y) = 1\)。
例如,如果小Y报出了 \(6\) ,由于 \(7\) 不能报,所以小L下一个需要报 \(8\);如果小Y报出了 \(33\),则由于 \(34 = 17 \times 2\),\(35 = 7 \times 5\) 都不能报,小L下一个需要报出 \(36\) ;如果小Y报出了 \(69\),由于 \(70 \sim 79\) 的数都含有 \(7\),小L下一个需要报出 \(80\) 才行。
现在小Y的上一个数报出了 \(x\),小L想快速算出他下一个数要报多少,不过他很快就发现这个游戏可比原版的游戏难算多了,于是他需要你的帮助。当然,如果小Y报出的 \(x\) 本身是不能报出的,你也要快速反应过来小Y输了才行。
由于小Y和小L玩了很长时间游戏,你也需要回答小L的很多个问题。
输入格式
第一行,一个正整数 \(T\) 表示小L询问的数量。
接下来 \(T\) 行,每行一个正整数 \(x\),表示这一次小Y报出的数。
输出格式
输出共 \(T\) 行,每行一个整数,如果小Y这一次报出的数是不能报出的,输出 \(-1\),否则输出小L下一次报出的数是多少。
样例 #1
样例输入 #1
4
6
33
69
300
样例输出 #1
8
36
80
-1
提示
【样例解释 #1】
这一组样例的前 \(3\) 次询问在题目描述中已有解释。
对于第 \(4\) 次询问,由于 \(300 = 75 \times 4\),而 \(75\) 中含有 \(7\) ,所以小Y直接输掉了游戏。
【数据范围】
对于 \(100\%\) 的数据,\(1 \le T \leq 2 \times {10}^5\),\(1 \le x \leq {10}^7\)。
思路
预处理一遍1-1e7的所有数,如果数中含有7,则它的所有倍数都标记为1;如果一个数不是含7数的倍数,则将他记为上一个不是含7数的倍数的答案。
AC代码
#include <bits/stdc++.h>
using namespace std ;
const int max_N=1e7+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int check (int n){
while(n>0){
if(n%10==7){
return 1;
}
n/=10;
}
return 0;
}
int main(){
vector <int > a(max_N);
vector <int > b(max_N);
int pre=0;
for(int i=1;i<max_N;i++){
if(check(i)==1){
for(int j=i;j<max_N;j+=i){
a[j]=1;
}
}
if(a[i]==0){
b[pre]=i;
pre=i;
}
}
int t=1;
cin>>t;
while(t--){
int x;
cin>>x;
if(a[x]==1){
cout<<-1<<endl;
}else{
cout<<b[x]<<endl;
}
}
return 0;
}
E: 修路
题目描述
辽宁大学去年重新铺设了银杏路,铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 \(n\) 块首尾相连的区域,一开始,第 \(i\) 块区域下陷的深度为 \(d_i\) 。
工人每天可以选择一段连续区间 \([L,R]\) ,填充这段区间中的每块区域,让其下陷深度减少 \(1\)。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 \(0\) 。
聪明的小Y想考考你,怎样设计方案可以在最短的时间内将整段道路的下陷深度都变为 \(0\) 。
输入格式
输入包含两行,第一行包含一个整数 \(n\),表示道路的长度。 第二行包含 \(n\) 个整数,相邻两数间用一个空格隔开,第 \(i\) 个整数为 \(d_i\) 。
输出格式
输出文件仅包含一个整数,即最少需要多少天才能完成任务。
样例 #1
样例输入 #1
6
4 3 2 5 3 5
样例输出 #1
9
提示
【样例解释】
一种可行的最佳方案是,依次选择:
\([1,6]\)、\([1,6]\)、\([1,2]\)、\([1,1]\)、\([4,6]\)、\([4,4]\)、\([4,4]\)、\([6,6]\)、\([6,6]\)。
【数据规模与约定】
对于 \(100\%\) 的数据,\(1 ≤ n ≤ 100000 , 0 ≤ d_i ≤ 10000\) 。
思路
思维题,设一个last,从左往右遍历,如果深度比last浅,则在修前一个坑的时候可以顺道一起修了,并更新last=该深度;如果深度比last深,则只能顺道修一部分,剩下要另外修,ans+=中间的差值(深度-last)。
AC代码
#include <bits/stdc++.h>
using namespace std ;
const int max_N=1e5+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int main(){
int n;
cin>>n;
int d[n+1];
for(int i=1;i<=n;i++){
cin>>d[i];
}
int last=0;
ll ans=0;
for(int i=1;i<=n;i++){
if(d[i]>last){
ans+=d[i]-last;
last=d[i];
}else{
last=d[i];
}
}
cout<<ans<<endl;
return 0;
}
F: Overstars的字符串
题目描述
现在\({\color{Gray}{Overstars}}\)有一串长度为\(n\)的仅由0与1构成的字符串。
他可以选择一段连续区间进行反转操作,使这个区间内的0变为1,1变为0。
\({\color{Gray}{Overstars}}\)只能选择进行一次反转操作或者不进行操作。
请输出可以得到的最长01间隔子串的长度,本题中将形如:\(1、0、01、10、101、010、1010\dots\)的字符串称为最长01间隔串。
子串的定义:字符串中任意个连续的字符组成的子序列。
输入格式
第一行一个整数\(n\)(\(1\le n \le 10^6\)),表示字符串的长度。
第二行一个长度为\(n\)的由0或1构成的字符串。
输出格式
一个整数,表示最长01间隔子串的长度。
样例 #1
样例输入 #1
5
10011
样例输出 #1
5
样例 #2
样例输入 #2
7
1101011
样例输出 #2
7
样例 #3
样例输入 #3
9
110001000
样例输出 #3
6
提示
所给第一组样例可以进行如下反转:
10011\(\rightarrow\) 10101
很明显此时的最长01间隔子串为10101本身。
第三组,注意加粗部分
110001000\(\rightarrow\) 110101000
思路
从左到右遍历记录每个最长01间隔子串的长度,两个最长01间隔子串之间相邻的数肯定是相同的,因此对于三个相邻的最长01间隔子串,改变中间的最长01间隔子串,三个最长01间隔子串将会合并成一个最长01间隔子串。
AC代码
#include <bits/stdc++.h>
using namespace std ;
const int max_N=1e5+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
int main(){
int n;
cin>>n;
string s;
cin>>s;
vector <int > a;
int count=1;
for(int i=1;i<s.size();i++){
if(s[i]!=s[i-1]){
count++;
}else{
a.push_back(count);
count=1;
}
}
a.push_back(count);
int ans=a[0];
if(a.size()>=2){
ans=max(ans,a[0]+a[1]);
}
for(int i=2;i<a.size();i++){
ans=max(ans,a[i-2]+a[i-1]+a[i]);
}
cout<<ans<<endl;
return 0;
}

浙公网安备 33010602011771号