2021年第十二届蓝桥杯大赛软件赛决赛C/C++大学A组 个人部分题解
Posted nefu_ljw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021年第十二届蓝桥杯大赛软件赛决赛C/C++大学A组 个人部分题解相关的知识,希望对你有一定的参考价值。
题目下载链接:https://download.csdn.net/download/ljw_study_in_CSDN/19403461
这次国赛,基本上大题都是暴力写的,填空题只写了前两个,老混子选手了
不一定是正确题解,本文抛砖引玉,欢迎大家讨论解法,批评指正~
文章目录
A题 纯质数(5分)
#include <bits/stdc++.h>
using namespace std;
const int N=20210605;
bool judge(int x)
{
if(x==0||x==1)return 0;
for(int i=2;i*i<=x;i++)
if(x%i==0)return 0;
return 1;
}
int main()
{
ios::sync_with_stdio(false);
int ans=0;
for(int i=1;i<=N;i++)
{
if(judge(i))
{
int x=i;
bool flag=1;
while(x)
{
if(!judge(x%10)){flag=0;break;}
x/=10;
}
if(flag)ans++;
}
}
printf("%d\\n",ans);
return 0;
}
答案:1903
B题 完全日期(5分)
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
bool vis[N];
int get_day(int y,int m)
{
if(m==2)
{
if(y%4==0&&y%100!=0)return 29; // 2020.02.29
else return 28; // 2021.02.28
}
else
{
if(m==1||m==3||m==5||m==7||m==8||m==10||m==12)return 31;
return 30;
}
}
int f(int x)
{
int s=0;
while(x)
{
s+=x%10;
x/=10;
}
return s;
}
int main()
{
for(int i=1;i<=1000;i++)
vis[i*i]=1;
int ans=0;
for(int y=2001;y<=2021;y++)
{
for(int m=1;m<=12;m++)
{
int allday=get_day(y,m);
for(int day=1;day<=allday;day++)
{
int sum=f(y)+f(m)+f(day);
if(vis[sum])ans++;
}
}
}
printf("%d\\n",ans);
return 0;
}
答案:977
C题 最小权值(10分)
不会…看到有人说猜一个完全二叉树的结论,然后记忆化搜索
更新:树形dp/记忆化搜索
D题 覆盖(10分)
不会
更新:状压dp
E题 123(15分)
试题 E: 123
时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分
【问题描述】
小蓝发现了一个有趣的数列,这个数列的前几项如下:
1, 1, 2, 1, 2, 3, 1, 2, 3, 4, ...
小蓝发现,这个数列前 1 项是整数 1,接下来 2 项是整数 1 至 2,接下来
3 项是整数 1 至 3,接下来 4 项是整数 1 至 4,依次类推。
小蓝想知道,这个数列中,连续一段的和是多少。
【输入格式】
输入的第一行包含一个整数 T,表示询问的个数。
接下来 T 行,每行包含一组询问,其中第 i 行包含两个整数 li 和 ri,表示
询问数列中第 li 个数到第 ri 个数的和。
【输出格式】
输出 T 行,每行包含一个整数表示对应询问的答案。
【样例输入】
3
1 1
1 3
5 8
【样例输出】
1
4
8
【评测用例规模与约定】
对于 10% 的评测用例,1 ≤ T ≤ 30, 1 ≤ li ≤ ri ≤ 100。
对于 20% 的评测用例,1 ≤ T ≤ 100, 1 ≤ li ≤ ri ≤ 1000。
对于 40% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ li ≤ ri ≤ 10^6。
对于 70% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ li ≤ ri ≤ 10^9。
对于 80% 的评测用例,1 ≤ T ≤ 1000, 1 ≤ li ≤ ri ≤ 10^12。
对于 90% 的评测用例,1 ≤ T ≤ 10000, 1 ≤ li ≤ ri ≤ 10^12。
对于所有评测用例,1 ≤ T ≤ 100000, 1 ≤ li ≤ ri ≤ 10^12。
类似于分块写的,分成中间若干个完整组,然后加上左右两边的部分组。预处理一下前 i i i组个数和前缀和,用二分找其位置。
按100%数据写的,期望别写挂
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const ll N=2e6+3;
ll a[N+10],sum[N+10];
ll get_id(ll x) // 得到组号
{
return (ll)(lower_bound(a+1,a+N+1,x)-a);
}
int main()
{
ios::sync_with_stdio(false);
for(ll i=1;i<=N;i++)
{
a[i]=(1+i)*i/2; // 前i组个数
sum[i]=sum[i-1]+a[i]; // 前i组的和
}
int T;cin>>T;
ll l,r,ans;
while(T--)
{
cin>>l>>r;
ll n1=get_id(l);
ll n2=get_id(r);
if(n1==n2) // 同一组
{
ll num=r-l+1;
ll x=l-a[n1-1];
ans=x*num+num*(num-1)/2;
}
else
{
ll num=a[n1]-l+1;
ll x=l-a[n1-1];
ll s1=x*num+num*(num-1)/2; // 前面部分
ll s2=sum[n2-1]-sum[n1]; // 中间的完整组
num=r-a[n2-1];
ll s3=num+num*(num-1)/2; // 最后部分
ans=s1+s2+s3;
}
printf("%llu\\n",ans);
}
return 0;
}
/*
999999999999 1000000000000
ans:176417
1000000000000 1000000000000
ans:88209
*/
F题 异或变换(15分)
试题 F: 异或变换
时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分
【问题描述】
小蓝有一个 01 串 s = s1 s2 s3 · · · sn。
以后每个时刻,小蓝要对这个 01 串进行一次变换。每次变换的规则相同。
对于 01 串 s = s1 s2 s3 · · · sn,变换后的 01 串 s′ = s1′s2′s3′· · · sn′为:
s1′ = s1;
si = si−1′ ⊕ si。
其中 a ⊕ b 表示两个二进制的异或,当 a 和 b 相同时结果为 0,当 a 和 b
不同时结果为 1。
请问,经过 t 次变换后的 01 串是什么?
【输入格式】
输入的第一行包含两个整数 n, t,分别表示 01 串的长度和变换的次数。
第二行包含一个长度为 n 的 01 串。
【输出格式】
输出一行包含一个 01 串,为变换后的串。
【样例输入】
5 3
10110
【样例输出】
11010
【样例说明】
初始时为 10110,变换 1 次后变为 11101,变换 2 次后变为 10011,变换 3
次后变为 11010。
【评测用例规模与约定】
对于 40% 的评测用例,1 ≤ n ≤ 100, 1 ≤ t ≤ 1000。
对于 80% 的评测用例,1 ≤ n ≤ 1000, 1 ≤ t ≤ 10^9。
对于所有评测用例,1 ≤ n ≤ 10000, 1 ≤ t ≤ 10^18。
找循环节,若某个字符串第二次出现就停止循环。当然这么处理之后也过不了全部数据,估计40%左右
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
unordered_map<string,ll>vis;
unordered_map<ll,string>mp;
string f(string s)
{
string res=s;
for(int i=1;s[i];i++)
res[i]=(s[i]-'0')^(s[i-1]-'0')+'0';
return res;
}
int main()
{
ios::sync_with_stdio(false);
string s;
int n;ll k;
cin>>n>>k>>s;
vis[s]=(ll)1;
mp[(ll)1]=s;
for(ll i=2;i<=k+1;i++) // k次
{
s=f(s);
if(!vis[s])
{
vis[s]=i;
mp[i]=s;
}
else // 找到循环节
{
ll pos=vis[s]; // 上一次出现的位置
ll ansk=(k+1-pos)%(i-pos);
s=mp[pos+ansk];
break;
}
}
printf("%s\\n",s.c_str());
return 0;
}
/*
5 1000000000000000000
10110
*/
G题 冰山(20分)
试题 G: 冰山
时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分
【问题描述】
一片海域上有一些冰山,第 i 座冰山的体积为 Vi。
随着气温的变化,冰山的体积可能增大或缩小。第 i 天,每座冰山的变化
量都是 Xi。当 Xi > 0 时,所有冰山体积增加 Xi;当 Xi < 0 时,所有冰山体积减
少 −Xi;当 Xi = 0 时,所有冰山体积不变。
如果第 i 天某座冰山的体积变化后小于等于 0,则冰山会永远消失。
冰山有大小限制 k。如果第 i 天某座冰山 j 的体积变化后 Vj 大于 k,则它
会分裂成一个体积为 k 的冰山和 Vj − k 座体积为 1 的冰山。
第 i 天结束前(冰山增大、缩小、消失、分裂完成后),会漂来一座体积为
Yi 的冰山(Yi = 0 表示没有冰山漂来)。
小蓝在连续的 m 天对这片海域进行了观察,并准确记录了冰山的变化。小
蓝想知道,每天结束时所有冰山的体积之和(包括新漂来的)是多少。
由于答案可能很大,请输出答案除以 998244353 的余数。
【输入格式】
输入的第一行包含三个整数 n, m, k,分别表示初始时冰山的数量、观察的
天数以及冰山的大小限制。
第二行包含 n 个整数 V1, V2, · · · , Vn,表示初始时每座冰山的体积。
接下来 m 行描述观察的 m 天的冰山变化。其中第 i 行包含两个整数 Xi
, Yi,意义如前所述。
【输出格式】
输出 m 行,每行包含一个整数,分别对应每天结束时所有冰山的体积之和
除以 998244353 的余数。
【样例输入】
1 3 6
1
6 1
2 2
-1 1
【样例输出】
8
16
11
【样例说明】
在本样例说明中,用 [a1, a2, · · · , an] 来表示每座冰山的体积。
初始时的冰山为 [1]。
第 1 天结束时,有 3 座冰山:[1, 1, 6]。
第 2 天结束时,有 6 座冰山:[1, 1, 2, 3, 3, 6]。
第 3 天结束时,有 5 座冰山:[1, 1, 2, 2, 5]。
【评测用例规模与约定】
对于 40% 的评测用例,n, m, k ≤ 2000;
对于 60% 的评测用例,n, m, k ≤ 20000;
对于所有评测用例,1 ≤ n, m ≤ 100000, 1 ≤ k ≤ 10^9, 1 ≤ Vi ≤ k, 0 ≤ Yi ≤ k, −k ≤ Xi ≤ k。
分成Vj − k个体积为1的冰山,会影响冰山个数和后续冰山的变化,这个地方没想到怎么优化处理…只写了暴力,水个40%数据
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e5+10,mod=998244353;
vector<ll>g;
vector<ll> f(vector<ll> t,ll x,ll y,ll k)
{
vector<ll>res;
ll sz=(ll)t.size();
for(ll i=0;i<sz;i++)
{
t[i]+=x;
if(t[i]>0) // 只保留大于0的
{
if(t[i]<=k)res.push_back(t[i]);
else // 分裂
{
res.push_back(k);
for(ll j=1;j<=t[i]-k;j++) // 没想到这里怎么优化
res.push_back((ll)1);
}
}
}
if(y>0)res.push_back(y);
return res;
}
int main()
{
ios::sync_with_stdio(false);
ll n,m,k,x,y;
cin>>n>>m>>k;
for(ll i=1;i<=n;i++)
{
cin>>x;
g.push_back(x);
}
for(ll i=1;i<=m;i++)
{
cin>>x>>y;
g=f(g,x,y,k);
ll sum=0;
for(ll j=0;j<g.size();j++)
sum=(sum+g[j])%mod;
printf("%lld\\n",sum);
}
return 0;
}
H题 翻转括号序列(20分)
试题 H: 翻转括号序列
时间限制: 2.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
给定一个长度为 n 的括号序列,要求支持两种操作:
1. 将 [Li, Ri] 区间内(序列中的第 Li 个字符到第 Ri 个字符)的括号全部翻
转(左括号变成右括号,右括号变成左括号)。
2. 求出以 Li 为左端点时,最长的合法括号序列对应的 Ri (即找出最大的
Ri 使 [Li, Ri] 是一个合法括号序列)。
【输入格式】
输入的第一行包含两个整数 n, m,分别表示括号序列长度和操作次数。
第二行包含给定的括号序列,括号序列中只包含左括号和右括号。
接下来 m 行,每行描述一个操作。如果该行为 “1 Li Ri”,表示第一种操作,
区间为 [Li, Ri] ;如果该行为 “2 Li” 表示第二种操作,左端点为 Li。
【输出格式】
对于每个第二种操作,输出一行,表示对应的 Ri。如果不存在这样的 Ri,请输出 0。
【样例输入】
7 5
((())()
2 3
2 2
1 3 5
2 3
2 1
【样例输出】
4
7
0
0
【评测用例规模与约定】
对于 20% 的评测用例,n, m ≤ 5000;
对于 40% 的评测用例,n, m ≤ 30000;
对于 60% 的评测用例,n, m ≤ 100000;
对于所有评测用例,1 ≤ n ≤ 10^6, 1 ≤ m ≤ 2 × 10^5。
这题看到有人说左右括号看成-1和1然后用树状数组维护找最大区间使其和为0然后再加限制条件保证左右能匹配上…然而我还是暴力,水一下20%数据
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10,inf=0x7f7f7f7f;
typedef long long ll;
int n,m;
char s[N];
int 第十二届蓝桥杯大赛软件赛决赛题解
2021 第十二届蓝桥杯大赛软件赛省赛(第二场),C/C++大学B组题解
2021 第十二届蓝桥杯大赛软件赛省赛(第二场),C/C++大学B组题解
2021 第十二届蓝桥杯大赛软件赛省赛,C/C++ 大学B组题解