牛客2025秋季算法编程训练联赛4-基础组
A 欧几里得
题目描述
欧几里得算法是一种求最大公约数的有效算法,在算法竞赛中很常用。
这个算法的 Python 实现如下:
def gcd(a,b):if b == 0:return areturn gcd(b,a%b)现在,如果已知 gcd(a,b) 共递归了 n次,求所有可能的a,b中满足a>b>=0且a+b最小的一组的a与b之和。
输入描述:
第一行一个整数,T。
接下来T行一行一个整数,n。
输出描述:
T行,每行一个整数,代表a+b。
示例1
输入
复制1 0
1 0输出
复制1
1说明
gcd(1,0) 由于 b=0,不会递归,即是递归0次。示例2
输入
复制1 1
1 1输出
复制3
3说明
gcd(2,1)会递归一次至gcd(1,0)。备注:
1≤T≤81
0≤n≤800≤n≤80
思路列出几个数就可以发现规律,类似于斐波那契博弈的递推
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans=INT_MAX;
pair<int,int>a[100];
signed main(){int t,n;cin>>t;while(t--){cin>>n;a[0].first=1,a[0].second=0;a[1].first=2,a[1].second=1;for(int i=2;i<=100;i++){a[i].first=a[i-1].first+a[i-1].second;a[i].second=a[i-1].first;}cout<<a[n].first+a[n].second<<endl;}//gcd(a,b);
}
B 括号序列
题目描述
注意:数据已加强并进行了rejudge
给出一个仅包含'[',']','(',')','{','}'六种字符的括号序列,判断其是否合法。
- 空串是一个合法的括号序列
- 如果A, B 都是合法的括号序列,那么AB也是合法的括号序列
- 如果A是合法的括号序列,(A) , [A], {A}都是合法的括号序列
输入描述:
一行一个字符串S,只包含题目中的六种括号字符输出描述:
输出为一行"Yes" 或"No"示例1
输入
复制(){}[]
(){}[]输出
复制Yes
Yes示例2
输入
复制({[]})
({[]})输出
复制Yes
Yes示例3
输入
复制([)]
([)]输出
复制No
No备注:
1≤∣S∣≤1000000
利用栈,遇到左括号就给压进去,遇到有括号就开始进行匹配
#include<bits/stdc++.h>
using namespace std;
string s;
stack<char>stk;
bool f(){for(char c:s){if(c=='('||c=='{'||c=='['){stk.push(c);}else{if(stk.empty()){return false;}else{char top=stk.top();if((c==')'&&top=='(')||(c=='}'&&top=='{')||(c==']'&&top=='[')){stk.pop();}else{return false;}}}}return stk.empty();
}
int main(){getline(cin, s);if(f()){cout<<"Yes";}else{cout<<"No";}}
C 子段乘积
题目描述
给出一个长度为 n 的数列 a1,a2,…,ana_1,a_2,\ldots,a_na1,a2,…,an,求其长度为 k 的连续子段的乘积对 998244353 取模余数的最大值。
输入描述:
第一行两个整数n,k。 第二行n个整数,a1,a2,…,ana_1,a_2,\ldots,a_na1,a2,…,an。输出描述:
输出一个整数,代表最大余数。示例1
输入
复制5 3 1 2 3 0 8
5 3 1 2 3 0 8输出
复制6
6说明
1∗2∗3mod 998244353=61*2*3\mod 998244353=61∗2∗3mod998244353=6备注:
1≤k≤n≤2∗105
0≤ai<9982443530 \le a_i <9982443530≤ai<998244353
思路
利用逆元,除法同余原理,滑动窗口,进行操作
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
int a[200030];
int power(int b,int n) {int ans=1;while(n>0) {if((n&1)==1) {ans=(ans*b)%mod;}b=(b*b)%mod;n>>=1;}return ans;
}
signed main() {int n,k;cin>>n>>k;for(int i=1;i<=n;i++) {cin>>a[i];}if(k==0){cout<<1<<endl;return 0;}int ans=1;int zero_count=0;for(int i=1;i<=k;i++) {if(a[i]==0)zero_count++;else ans=(ans*a[i])%mod;}int res=(zero_count==0)?ans:0;for(int i=k+1;i<=n;i++){if(a[i-k]==0) zero_count--;else{ans=(ans*power(a[i-k],mod-2))%mod;}if(a[i]==0) zero_count++;else ans=(ans*a[i])%mod;if(zero_count==0){res=max(res,ans);}else{ res=max(res,0LL);}}cout <<res%mod<<endl;return 0;
}
D 子段异或
题目描述
输入一个数列a,你需要输出其中异或值为0的不同子段的数量。一个子段 [l,r] (1≤l≤r≤n1 \le l \le r \le n1≤l≤r≤n)的异或值为al⊕al+1⊕al+2⊕…⊕ara_l \oplus a_{l+1} \oplus a_{l+2} \oplus \ldots\oplus a_ral⊕al+1⊕al+2⊕…⊕ar,其中⊕\oplus⊕符号代表异或运算。
两个子段被视为相同的,当且仅当其开始和结束位置均对应相同。
输入描述:
第一行一个整数 n ,代表数列长度。
第二行 n 个整数,代表数列。
输出描述:
输出一个整数,代表答案。示例1
输入
复制5 1 2 3 2 1
5 1 2 3 2 1输出
复制2
2说明
子段 [1,3] 和子段 [3,5] 是合法子段。备注:
n≤200000,0≤ai≤2^30−1
思路 找到prefix[l-1] 与prefix[r]相等,这俩异或肯定为0,然后prefix[r]一定也包括prefix[l-1]所以就可以知道l到r一定异或值为0
#include<bits/stdc++.h>
using namespace std;
long long prefix;
int a[200020];
int main(){int n;cin>>n;unordered_map<long long ,long long>mp;mp[0]=1;for(int i=1;i<=n;i++){cin>>a[i];prefix^=a[i];mp[prefix]++;}long long ans=0;for(auto &p : mp){long long c=p.second;ans+=c*(c-1)/2;}cout<<ans;}
E 最小表达式
题目描述
给出一个包含数字1-9和加号的字符串,请你将字符串中的字符任意排列,但每种字符数目不变,使得结果是一个合法的表达式,而且表达式的值最小。输出那个最小表达式的值
合法的表达式的定义如下:
- 一个数字,如233,是一个合法的表达式
- A + B是合法的表达式,当且仅当 A , B 都是合法的表达式
保证给出的表达式经过重排,存在一个合法的解。
输入描述:
一行输入一个字符串,仅包含数字1-9和加号+。
字符串的长度小于5∗1055*10^55∗105。
输出描述:
一行输出一个数字,代表最小的解。示例1
输入
复制111+1
111+1输出
复制22
22说明
11+11=22示例2
输入
复制9998765432111
9998765432111输出
复制1112345678999
1112345678999示例3
输入
复制12+35
12+35输出
复制38
38说明
25+13 = 38
示例4
输入
复制23984692+238752+2+34+
23984692+238752+2+34+输出
复制5461
5461说明
嗯,这个答案是可以得到的
备注:
注意,答案长度可能长达500000个字符。
思路
就是利用贪心加高精度,其实就是看一下+有几个然后分成几组数,然后把最小的数字都扔给最高位,再利用高精度相加
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500010;
int res[MAXN];
int main() {string s;cin>>s;vector<char>digits;int k=0;for(char c:s) {if(c=='+'){k++;}else{digits.push_back(c);}}sort(digits.begin(),digits.end());int n=digits.size();int m=k+1;int len=n/m;int r=n%m;int start=0;if(r>0){//有r组数需要多一位数 int S1=0;for(int i=0;i<r;i++){S1+=digits[start++]-'0';}res[len]+=S1;//所以从len开始 }for(int j=len-1;j>=0;j--){int S=0;for(int i=0;i<m;i++) {//先把最小的数给最高位,所以从len-1开始 S+=digits[start++]-'0';}res[j]+=S;}int L=len+10;//防止进位时候越界 for (int i=0;i<L-1;i++) {if(res[i]>=10){res[i+1]+=res[i]/10;res[i]%=10;}}int high=L-1;while(high>0&&res[high]==0){high--;}for(int i=high;i>=0;i--){cout<<res[i];}cout<<endl;return 0;
}
