第十三届蓝桥杯国赛 C++ C组 F 题Python B组 E 题——近似GCD(AC)
Posted 执 梗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十三届蓝桥杯国赛 C++ C组 F 题Python B组 E 题——近似GCD(AC)相关的知识,希望对你有一定的参考价值。
目录
1.近似GCD
1.题目描述
小蓝有一个长度为 n n n 的数组 A = ( a 1 , a 2 , ⋯ , a n ) A=\\left(a_1, a_2, \\cdots, a_n\\right) A=(a1,a2,⋯,an), 数组的子数组被定义为从 原数组中选出连续的一个或多个元素组成的数组。数组的最大公约数指的是数 组中所有元素的最大公约数。如果最多更改数组中的一个元素之后, 数组的最 大公约数为 g g g, 那么称 g g g 为这个数组的近似 GCD。一个数组的近似 GCD 可能 有多种取值。
具体的, 判断 g g g 是否为一个子数组的近似 GCD 如下:
如果这个子数组的最大公约数就是 g g g, 那么说明 g g g 是其近似 GCD。
在修改这个子数组中的一个元素之后 (可以改成想要的任何值), 子数 组的最大公约数为 g g g, 那么说明 g g g 是这个子数组的近似 GCD。
小蓝想知道, 数组 A A A 有多少个长度大于等于 2 的子数组满足近似 GCD 的值为 g g g.
2.输入格式
输入的第一行包含两个整数
n
,
g
n,g
n,g,用一个空格分隔,分别表示数组
A
A
A 的长度和
g
g
g 的值。
第二行包含
n
n
n 个正数
a
1
,
a
2
,
⋯
,
a
n
,
a_1,a_2,⋯,a_n,
a1,a2,⋯,an, 相邻两个整数之间用一个空格分隔。
3.输出格式
输出一行包含一个整数表示数组
A
A
A 有多少个长度大于等于 2
的子数组的近 似 GCD 的值为
g
g
g 。
4.样例输入
5 3
1 3 6 4 10
5.样例输出
5
6.数据范围
2 ≤ n ≤ 1 0 5 , 1 ≤ g , a i ≤ 1 0 9 。 2≤n≤10^5,1≤g,ai≤10^9。 2≤n≤105,1≤g,ai≤109。
7.原题链接
2.解题思路
首先,如果一个数是g
的倍数,那我们称其为符合条件的数。如果一个数组的近似GCD为
g
g
g,那么该数组最多只能有一个数不符合条件。为什么呢?因为如果只有一个不符合条件的数话,我们将其变为g
,那么该数组的GCD将为g
。如果数组全部符合条件呢?那我们只需要随便将其中一个数变为g
,该数组的GCD也将为g
。
那么现在问题就转换为存在多少个长度大于2
的子数组使得子数组内最多只存在一个不符合条件的数,这个问题我们可以使用双指针解决。右指针r
遍历数组的每一个数,左指针l
将是以r
将作为子数组的右端点的情况下,左端点能最远能到达的距离,也就是使得
[
l
,
r
]
[l,r]
[l,r]区间最多只存在一个不符合条件的数,且
l
l
l 和
r
r
r 之间的距离尽可能长。这样的话,数组
[
l
,
r
]
[l,r]
[l,r],
[
l
+
1
,
r
]
[l+1,r]
[l+1,r],
[
l
+
2
,
r
]
[l+2,r]
[l+2,r]…
[
r
−
1
,
r
]
[r-1,r]
[r−1,r]都是符合条件的答案,总共是r-l
个。对于数组的每一个数我们都将其作为r
后,累加答案即可。
我们考虑变换数组的值,如果其是符合条件的数,我们将其值赋为1
,否则赋为0
,对于区间
[
l
,
r
]
[l,r]
[l,r]是否为符合条件的子数组,只需要判断
s
u
m
[
l
,
r
]
sum[l,r]
sum[l,r]是否大于等于
r
−
l
r-l
r−l。求区间和
s
u
m
sum
sum,我们可以使用前缀和数组直接获取,但由于是双指针,也可以同时维护,这里代码使用了前缀和数组。
时间复杂度: O ( n ) O(n) O(n)。
3.Ac_code
1.C++
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> PII;
#define pb(s) push_back(s);
#define SZ(s) ((int)s.size());
#define ms(s,x) memset(s, x, sizeof(s))
#define all(s) s.begin(),s.end()
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 200010;
int n, g;
void solve()
cin >> n >> g;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++i)
int x;
cin >> x;
a[i] = (x % g == 0);
a[i] += a[i - 1];
int l = 0;
LL ans = 0;
for (int r = 2; r <= n; ++r)
while (l + 1 < r && a[r] - a[l] < r - l - 1) l++;
ans += r - l -1;
cout << ans << '\\n';
int main()
ios_base :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t = 1;
while (t--)
solve();
return 0;
2.Python
n,g=map(int,input().split())
a=list(map(int,input().split()))
a=[0]+a
ans=0#记录上一个不符合条件的数
last=0#记录符合条件子数组的左区间
l=1
for r in range(1,n+1):
if a[r]%g!=0:
l=last+1
last=r
ans=ans+(r-l)
print(ans)
第十三届蓝桥杯国赛真题 PythonB组 复盘以及获奖感言(国一!!!)
第十三届蓝桥杯国赛真题 PythonB组 复盘以及获奖感言(国一)
文章目录

🏆 获奖感言
国一开心! 国一开心! **国一开心!**重要的词说三遍,这个国赛确实让我有点惊喜,我居然拿到国赛一等奖!!!
从今年年初开始,慢慢的捡起曾经的数据结构的知识以及很多算法的知识,努力学习,在5月的时候,成功拿到了省一等奖,当时其实我都感觉我要凉了,感觉彻底没什么希望了,只希望自己能够拿一个省二吧,但是我的目标一开始就是国赛,不过不是国一,而是国二。
我和我的朋友努力学习,互相交流,我觉得在打比赛的时候,在学习的时候,有一个志同道合的朋友是很好的,也有很多人会加群,然后不断交流学习,这样的学习方式我感觉是不错的,大家可以互相思考,互相借鉴对方的思路,互相分享一些不错的知识点,这样我们就可以达到教学相长也的情况,而且大家也是一起努力的,一起比赛,最后我和我朋友分别我是国一,他国二,可谓是皆大欢喜。
其实蓝桥杯相对很多赛事来说,获奖的比例还是高的,比如在省一,可以有70%的人获奖,当然省一只有10%,在国赛,取前5%的为国一,15%为国二,20%为国三,不过实际上是加和的,也就是5+15+20=45,国赛有45%获奖,其他人只要不是0分都可以评个简单的优秀奖,不虚此行哈哈哈
随着时代的发展,蓝桥杯也不再是暴力杯了,特别今年开始,填空题只有2道,投机取巧的机会比较少了,我省一填空题只作出一道,但是国赛填空题好像一道都没有对,感觉还是慢慢变难了,也听说A组的题目也是很难的,所以慢慢蓝桥杯也调整了题目的分布和难度,而不仅仅是暴力搜索的解题,但是不可置疑的是,暴力解题还是可以得到一部分的分的。
总的来说,这次蓝桥杯之行还是圆满的,拿到了国赛的一等,为我的前途铺了层砖,也希望之后的大家也努努力,你们也一定可以的
试题A: 斐波那契与7
思路
这道题来说,其实看起来是很简单的,所以我想的也很简单,就是穷举,然后%10,最后一项为7的就+1,但是这个数据太大了,我运行了好久好久都没有出来,最后我利用C++去写这道题,然后也运行了2h以上,得到一个答案。
但是后面我发现,如果每次加的时候都%10,得到的结果不一定是正确的,所以最后这道题我应该是错误的,填空题,太难了呀,这穷举不出来啊。本来还想着用快速幂,不过最后还是放弃了。
代码
a,b=1,1
n = 202202011200
ans = 0
for i in range(n-2):
a,b = b%10,(a+b)%10
## print(b)
if b%10 == 7:
ans += 1
print(ans)
试题B: 小蓝做实验
思路
这道题来说,很简单,利用线性筛或者是埃氏筛,但是问题就是,我用了线性筛,虽然对于线性筛来说,我们的计算素数是很快的,但是整个数据其实还是很大很大,我朋友跟我说,其实我们先应该筛选,这个范围是10的3次方到10的12次方,总的来说,就是数据太大,用的方法稍微的有点不对劲,结果还是没跑出来,我python真的很久,绝望!!!
代码
n = 100000000
isprime = [False]*(n+1)
primes = [0]*(n+1)
cnt = 0
for i in range(2,n):
if not isprime[i]:
primes[cnt] = i
cnt += 1
for j in range(cnt):
if i*primes[j] > n: break
isprime[i*primes[j]] = True
if i%primes[j] == 0: break
primes = primes[:cnt]
res = 0
f = open('primes.txt')
data = f.readlines()
print(len(data))
for i in range(len(data)):
x = data[i].replace('/n','')
x = int(x)
if x in primes:
res += 1
if i%1000 == 0:
print(i,res)
print(res)
试题C: 取模
样例输入
3
1 2
5 2
999 99
样例输出
No
No
Yes
提示
对于 20% 的评测用例,T ≤ 100 ,n, m ≤ 1000;
对于 50% 的评测用例,T ≤ 10000 ,n, m ≤ 105;
对于所有评测用例,1 ≤ T ≤ 105 ,1 ≤ n ≤ 109 ,2 ≤ m ≤ 109。
思路
这道题完全没有什么思路,直接暴力即可,时间有限,还不如做下一道题
(后面来看,暴力还是能拿一半的分的,不亏不亏 https://www.dotcpp.com/oj/problem2735.html)
代码
t = int(input())
for i in range(t):
n,m = map(int,input().split())
flag = False
for x in range(1,m):
for y in range(x+1,m+1):
if n%x == n%y:
flag = True
break
if flag:
break
if flag:
print('Yes')
else:
print('No')
试题D:内存空间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SUiQHcY0-1656651026729)(C:\\Users\\86137\\AppData\\Roaming\\Typora\\typora-user-images\\image-20220701111108884.png)]
样例输入1
1
long[] nums=new long[131072];
样例输出1
1MB
样例输入2
4
int a=0,b=0;
long x=0,y=0;
String s1=”hello”,s2=”world”;
long[] arr1=new long[100000],arr2=new long[100000];
样例输出2
1MB538KB546B
思路
这道题,我觉得单纯就是对字符串进行一个操作,我们可以看到有两种类型,我们分别对两种类型进行操作
不过每一种类型来说,首先,我们都是要将我们的数据分开,相同的特点就是,他们都是以逗号进行分割的,所以我们可以用split(‘,’)很简单的分开。然后根据不同的int,char以及string来判断字符的大小和长度即可。
对于第二种类型来说,稍微复杂一点,但是思路还是很清晰的,就是我们可以找到左右两个中括号,然后对立面进行计数,而且他仅仅有两种类型,分别是int和long,这样的判断就更简单了。
最后我们可以得到我们的空间大小,但是我们需要进行化简,根据1GB=1024MB,1MB=1024KB,1KB=1024B,得到最后的结果,可以说,这道题是我比较有信心的题。
代码
t = int(input())
res = 0
for i in range(t):
s = input()
if '[]' in s:
a = s.split(',')
# print(a)
if s[0] == 'i':
for x in a:
left = x.rfind('[')
right = x.rfind(']')
ans = int(x[left+1:right])
print(ans)
res += ans*4
pass
elif s[0] == 'l':
for x in a:
left = x.rfind('[')
right = x.rfind(']')
ans = int(x[left+1:right])
res += ans*8
else:
a = s.split(' ')
tp,var = a[0],a[1]
if tp == 'int':
x = len(a[1].split(','))
res += x*4
elif tp == 'long':
x = len(a[1].split(','))
res += x*8
elif tp == 'String':
x = a[1].split(',')
for y in x:
res += len(y.split('=')[1]) - 2
res -= 1
GB = res//(1024*1024*1024)
MB = res//(1024*1024) - GB*1024
KB = res//(1024) - GB*1024*1024 - 1024*MB
B = res%1024
if GB != 0:
print('%dGB'%GB,end='')
if MB != 0:
print('%dMB'%MB,end='')
if KB != 0:
print('%dKB'%KB,end='')
if B != 0:
print('%dB'%B,end='')
试题E: 近似GCD
样例输入
5 3
1 3 6 4 10
样例输出
5
提示
满足条件的子数组有 5 个:
[1, 3]:将 1 修改为 3 后,这个子数组的最大公约数为 3 ,满足条件。
[1, 3, 6]:将 1 修改为 3 后,这个子数组的最大公约数为 3 ,满足条件。
[3, 6]:这个子数组的最大公约数就是 3 ,满足条件。
[3, 6, 4]:将 4 修改为 3 后,这个子数组的最大公约数为 3 ,满足条件。
[6, 4]:将 4 修改为 3 后,这个子数组的最大公约数为 3,满足条件。
对于 20% 的评测用例,2 ≤ n ≤ 102;
对于 40% 的评测用例,2 ≤ n ≤ 103;
对于所有评测用例,2 ≤ n ≤ 105 ,1 ≤ g, ai ≤ 109。
思路
这道题来说的话,其实有点复杂,我也不知道我的算法对不对。我一开始想着是否可以用DP解决,但是没有一个固定的思路,最后我直接穷举两端,然后对数据进行求gcd,如果发现有数据算出来不是期望的g,那我就进行修改,如果修改两次,就直接退出,说明这个子数组是不符合的。
这道题最后,应该也是暴力得了些许分,肯定不是满分的,也可以去测试一下https://www.dotcpp.com/oj/problem2727.html
代码
n,g = map(int,input().split())
a = list(map(int,input().split()))
from math import gcd
res = 0
for i in range(n-1):
for j in range(i+1,n):
x = a[i:j+1]
ans = g
flag = True
flag2 = True
# 判断所有的和
for y in x:
if y%g != 0:
if flag:
flag = False
y = g
else:
flag2 = False # 修改第二次
break
ans = gcd(ans,y)
if ans != g:
break
# print(x,ans)
if ans == g and flag2:
res += 1
print(res)
试题F: 交通信号
样例输入
4 4 1 4
1 2 1 2 6
4 2 1 1 5
1 3 1 1 1
3 4 1 99 1
样例输出
11
思路
这道题其实可以用迪杰斯特拉算法,构建双向边进行求解,但是考试的时候并没有想出来,所以最简单的可以拿分的就是,print(-1)
代码
print(-1)
试题G: 点亮
思路
没思路,写不出来,不过可以穷举2x2的方格捞分
代码
暂无
试题H: 打折
样例输入
2 2
1 2 89 1
1 97
3 4 77 1
2 15
样例输出
101
提示
对于 40% 的评测用例,n, m ≤ 500 ,si ≤ ti ≤ 100 ,∑ ci ≤ 200000 ;
对于 70% 的评测用例,n, m ≤ 5000 ,∑ ci ≤ 200000 ;
对于所有评测用例,1 ≤ n, m ≤ 100000,1≤ ci≤ n , ∑ ci ≤ 400000 ,1 ≤ si ≤ ti ≤ 109 ,1 < pi < 100 ,1 ≤ aj ≤ n ,1 ≤ bj ≤ 109 。
思路
这道题,就是一个暴力,找一下每个时间段中费用最低的,然后全部买一遍,最后遍历取最大的结果
这应该不是正解,但是考试的时间那时候还是比较紧张,我就先简单暴力写一下。
这道题最后,应该也是暴力得了些许分,肯定不是满分的,也可以去测试一下https://www.dotcpp.com/oj/problem2730.html
代码
n,m = map(int,input().split())
from collections import defaultdict
prices = defaultdict(list)
days = 0
for i in range(n):
s,t,p,c = map(int,input().split())
days = max(days,t)
for j in range(c):
a,b = map(int,input().split())
price = b*p//100
prices[a].append([price,s,t])
prices[a].append([b])
##print(prices)
min_price = float('inf')
for day in range(days):
res = 0
for k,v in prices.items():
v.sort()
# print(v)
for p in v:
if len(p) == 3:
if p[1] <= day <= p[2]:
ans = p[0]
break
else:
ans = p[0]
break
res = res + ans
## print(res)
min_price = min(res,min_price)
print(min_price)
试题I: owo
思路
这道题的思路也是穷举,但是这里面用了一下全排列函数,然后将数据排序,判断最长的结果。
时间有限,也没想好有什么好的方法,不过这个肯定能拿10+分的,40+分不知道有没有机会
代码
n = int(input())
def get_owo(sr):
l = len(sr)
x = sr.find('owo')
ans = 0
if x == -1:
return 0
else:
ans = 1
for i in range(x+2,l,2):
# print(sr[i:i+3])
if sr[i:i+3] == 'owo':
ans += 1
return ans
a = []
from itertools import permutations
for i in range(n):
s = input()
a.append(s)
res = 0
b = list(set(permutations(a)))
for x in b:
sr = ''.join(x)
## print(sr)
ans = get_owo(sr)
res = max(res,ans)
print(res)
试题J: 替换字符
样例输入
abcaaea
4
1 7 c e
3 3 e b
3 6 b e
1 4 a c
样例输出
cbecaea
提示
对于 40% 的评测用例,|s|, m ≤ 5000;
对于所有评测用例,1 ≤ |s|, m ≤ 105 ,1 ≤ li ≤ ri ≤ |s| ,xi 不等于 yi ,其中 |s| 表示字符串 s 的长度。
思路
这道题,直接就是区间修改,感觉是线段树,但是好像模板套的又不是很可以,之后就没有一个很好的想法了
最后利用python将部分字母进行替换,我们就可以直接用replace,用replace就可以简单的过了一部分的数据,应该是40%的数据
这道题最后,应该也是暴力得了些许分,也可以去测试一下https://www.dotcpp.com/oj/problem2739.html
代码
s = input()
n = int(input())
for _ in range(n):
l,r,x,y = input().split()
l,r = int(l),int(r)
tmp = s[l-1:r].replace(x,y)
## print(tmp)
s = s[:l-1] + tmp + s[r:]
# print(s)
print(s)
以上是关于第十三届蓝桥杯国赛 C++ C组 F 题Python B组 E 题——近似GCD(AC)的主要内容,如果未能解决你的问题,请参考以下文章
第十三届蓝桥杯国赛真题 PythonB组 复盘以及获奖感言(国一!!!)
蓝桥杯国赛真题21Scratch水果拼图 第十三届蓝桥杯 图形化编程scratch国赛真题和答案讲解
蓝桥杯国赛真题22Scratch镜像画笔 第十三届蓝桥杯 图形化编程scratch国赛真题和答案讲解