2023秋校赛

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

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\) 天。

一个年份是闰年当且仅当它满足下列两种情况其中的一种:

  1. 这个年份是 \(4\) 的整数倍,但不是 \(100\) 的整数倍;
  2. 这个年份是 \(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;
}

posted @ 2023-10-10 21:45  Beater_1117  阅读(60)  评论(0)    收藏  举报