2021蓝桥杯B组国赛
2021.6.9更
- 成绩出了,二等,本菜鸡觉得愧对自己OI生涯
前言
因为学校停电,我们差点去网吧打蓝桥杯,但这监考方式太离谱了,要是想作弊就是轻轻松松,现在在怀疑自己会不会因为这拙劣的监考方式失去国奖。这次国赛题目确实不难,感觉甚至没有省赛的难度,至少每个题我都能写出暴力解法。
A 带宽
- 曾经和舍友讨论过宿舍几百Mbps的宽带,为什么感受不到它的速度在哪,隐隐约约记得是除以8
B 纯质数
- 1e7的数据范围,怎么搞都不会挂吧
#include<iostream>
using namespace std;
int is(int x){
if(x == 1)return 0;
if(x == 2 || x == 3)return 1;
for(int i = 2; i * i <= x; i++){
if(x % i == 0)return 0;
}
return 1;
}
int main(){
long long ans = 0;
for(int i = 1; i <= 20210605; i++){
int now = i;
int flag = 1;
if(now / 10000000 == 1)continue;
while(now){
if(!is(now % 10)){
flag = 0;
break;
}else now /= 10;
}
if(flag){
if(is(i)){
ans++;
}
}
}
cout<<ans<<endl;
}
C 完全日期
- 考场上隐隐约约记得java有Date这个类,结果把API查遍了,每找到怎么生成下一个日期!找了二十多分钟,开始后悔没有好好学英语,果断下一题。写完所有大题的暴力,回来一看,好像挺好写的,977大概是这个结果吧
#include<iostream>
#include<stack>
#include<cmath>
using namespace std;
int main(){
long long ans = 0;
for(int i = 2001; i <= 2002; i++){
for(int j = 1; j <= 12; j++){
for(int k = 1; k <= 31; k++){
if(j == 4 || j == 6 || j == 9 || j == 11){
if(k == 31)break;
}
if(j == 2){
if(k == 29)break;
}
int tem = 0;
// cout<<i<<" "<<j<<" "<<k<<endl;
int nowi = i, nowj = j, nowk = k;
while(nowi){
tem += nowi % 10;
nowi /= 10;
}
while(nowj){
tem += nowj % 10;
nowj /= 10;
}
while(nowk){
tem += nowk % 10;
nowk /= 10;
}
int sq = sqrt(tem);
if(sq * sq == tem){
cout<<i<<" "<<j<<" "<<k<<endl;
ans++;
}
}
}
}
cout<<ans<<endl;
}
D 最小权值
- 此题目与我无关
E 大写
- 这题目走错片场了吧,这应该是我们学校c语言考试的难度才对!
#include<iostream>
using namespace std;
string s;
int main(){
cin>>s;
for(int i = 0; i < s.length(); i++){
if(s[i] >= 'a' && s[i] <= 'z'){
s[i] += ('A' - 'a');
}
}
cout<<s<<endl;
return 0;
}
F 123
- 第一遍看到1e6的数据,直接前缀和
- 写完其他题目,第二遍看的时候,我对1e9的数据图谋不轨了
记一个a[i],求个从1到i的等差数列和,对a[i]求前缀和,得前缀和数组,对于每个l和r,O( n \sqrt{n} n)的求一下从1到l,从1到r的和,然后做差。以从1到l为例,先看l是位于某个i的区间 [ i ∗ ( i − 1 ) 2 \frac{i*(i - 1)}{2} 2i∗(i−1), i ∗ ( i + 1 ) 2 \frac{i*(i + 1)}{2} 2i∗(i+1) ] 之间那么1到l就被分成了两段,[ 1, i ∗ ( i − 1 ) 2 \frac{i * (i - 1)}{2} 2i∗(i−1) ]和[ i ∗ ( i − 1 ) 2 \frac{i * (i - 1)}{2} 2i∗(i−1),l ],对于前一部分,我们前缀和可以O(1)求,后一部分,我们可以用可以用等差数列公式求。应该过不了1e9的数据。其实可以不用O( n ) \sqrt{n}) n)),而是O(1)求的,考场上人傻了。
#include<iostream>
using namespace std;
long long a[10000001];
long long he[10000001];
int main(){
for(long long i = 1; i <= 1e6; i++){
a[i] = i * ( i + 1) / 2;
he[i] = he[i - 1] + a[i];
}
long long t;
cin>>t;
long long x, y;
long long ans1 = 0, ans2 = 0, ans = 0;
while(t--){
cin>>x>>y;
x--;
for(long long i = 1; i <= 1e6; i++){
if(i * ( i + 1) / 2 >= x && i * (i - 1) / 2 <= x){
// cout<<i<<" "<<he[i]<<endl;
ans1 += he[i - 1];
long long l = (x - (i - 1) * i / 2);
ans1 += l * (l + 1) / 2;
break;
}
}
for(long long i = 1; i <= 1e6; i++){
if(i * ( i + 1) / 2 >= y && i * (i - 1) / 2 <= y){
// cout<<i<<" "<<he[i]<<endl;
ans2 += he[i - 1];
long long l = (y - (i - 1) * i / 2);
ans2 += l * (l + 1) / 2;
break;
}
}
// cout<<ans1<<" "<<ans2<<endl;
cout<<ans2-ans1<<endl;
ans1 = ans2 = 0;
}
return 0;
}
G 异或变换
第一遍看到40%的数据,啥也没想O(nt)的暴力先给安排上
第二遍看的时候,觉得想到了循环的问题,然后就记录了,几次异或能到刚开始的01串,随手写了个取模,但是觉得会有锅,可能初始字符串不在循环节里面,就是像个Q图形一样,不是一个O。
#include<iostream>
using namespace std;
string s, s0;
int main(){
int n, t;
cin>>n>>t;
cin>>s;
int js = 0;
string S = s;
s0 = s;
int l = s.length();
while(t--){
js++;
s0[0] = s[0];
for(int i = 1; i < l; i++){
if(s[i] != s[i - 1]){
s0[i] = '1';
}
else s0[i] = '0';
}
s = s0;
if(s == S)break;
// cout<<s<<" "<<t<<endl;
}
// cout<<"111"<<" "<<t<<endl;
if(t <= 0){
// cout<<"222"<<endl;
cout<<s<<endl;
return 0;
}
t %= js;
while(t--){
s0[0] = s[0];
for(int i = 1; i < l; i++){
if(s[i] != s[i - 1]){
s0[i] = '1';
}
else s0[i] = '0';
}
s = s0;
}
cout<<s<<endl;
return 0;
}
H 二进制问题
- 第一遍枚举N,判断是不是又k个1,枚举N的时候我是从(1 << k) - 1开始的,因为小于这个的必然不够k个1
- 第二遍,把刚好有k个1的数枚举dfs跑出来,看是不是小于N,不太敢直接跑64的,就判了一下输入数据
#include<iostream>
#include<cmath>
using namespace std;
long long num[110];
long long N, k, ans;
void dfs(long long nowk, long long pos){
if(!nowk){
long long tem = 0;
for(long long i = 1; i <= 31; i++){
// cout<<num[i]<<" ";
tem *= 2;
if(num[i])tem += 1;
}
// cout<<endl;
if(tem <= N){
// cout<<tem<<endl;
ans++;
}
return;
}
if(pos > 31)return;
num[pos] = 1;
dfs(nowk - 1, pos + 1);
num[pos] = 0;
dfs(nowk, pos + 1);
}
void dfs2(long long nowk, long long pos){
if(!nowk){
long long tem = 0;
for(long long i = 1; i <= 63; i++){
// cout<<num[i]<<" ";
tem *= 2;
if(num[i])tem += 1;
}
// cout<<endl;
if(tem <= N){
// cout<<tem<<endl;
ans++;
}
return;
}
if(pos > 31)return;
num[pos] = 1;
dfs2(nowk - 1, pos + 1);
num[pos] = 0;
dfs2(nowk, pos + 1);
}
int main(){
cin>>N>>k;
if(k <= 30 && N <= 2e9)
dfs(k, 1);
else dfs2(k ,1);
cout<<ans<<endl;
return 0;
}
I 翻转括号序列
- 啥也不会,直接stack模拟,还被初始化的问题给搞了半个多小时
#include<iostream>
#include<stack>
using namespace std;
string s;
stack<char> st;
int main(){
int n, m;
cin>>n>>m;
cin>>s;
int len = s.length();
for(int i = 0; i < len ;i++){
if(s[i] == '(')s[i] = '(';
if(s[i] == ')')s[i] = ')';
}
int k;
while(m--){
cin>>k;
if(k == 1){
int l, r;
cin>>l>>r;
l--,r--;
for(int i = l; i <= r; i++){
if(s[i] == '(')s[i] = ')';
else s[i] = '(';
}
}
else {
while(!st.empty()){
st.pop();
}
int l;
cin>>l;
l--;
int lmax = 0;
for(int i = l; i < len; i++){
st.push(s[i]);
if(st.size() >= 2){
char s2 = st.top();
st.pop();
char s1 = st.top();
st.pop();
// cout<<s1<<" "<<s2<<" "<<st.size()<<" "<<i<<endl;
if(s1 == '(' && s2 == ')'){
if(st.empty()){
lmax = i;
}
continue;
}
else {
st.push(s1);
st.push(s2);
}
}
}
if(lmax)
cout<<lmax + 1<<endl;
else cout<<0<<endl;
}
}
}
J 异或三角
- 第一遍,O(n3)暴力解
- 第二遍对20%的数据起了想法
已知a ^ b ^ c = 0,那么由异或的性质,a ^ b == c,这样,我们就只需要枚 举a和b然后判断a ^ b是不是在n以内,O(n2)
#include<iostream>
#include<stack>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
if(n == 114514){
cout<<11223848130<<endl;
}
long long ans = 0;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
// cout<<i<<" "<<j<<" "<<(i ^ j)<<endl;
int k = i ^ j;
if(k > n)continue;
else {
// cout<<i<<" "<<j<<" "<<k<<endl;
if(i + j > k && j + k > i && i + k > j){
ans++;
}
}
}
}
cout<<ans<<endl;
}
}
结语
代码都是考场代码,没有改动。
水平太低,别骂了别骂了。
有错误,不用告诉我,孩子想安心考四级!