2026牛客寒假算法基础集训营1 题解
L. Need Zero
难度:800
题意:
给定一个数字 \(n\) 寻找一个数字 \(x\) 使得 \(x \times n \ mod \ 10 = 0\)。
题解:
简单看出,\(gcd(n,10)\) 可以寻找出 \(n\) 中的构建 \(10\) 的因子,使用 \(10\) 除 \(gcd(n,10)\) 即可知道缺少的因子 \(x\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
void sol(){
int n; cin >> n;
cout << 10 / gcd(n, 10LL) << endl;
}
signed main() {
int t = 1; //cin >> t;
while (t --) sol();
return 0;
}//L
C. Array Covering
难度:1000
题意:
给定一个数组 \({a_{1\dots n}}\) 可以进行操作:选取 \(l,j(1 \leq l<j\leq n)\) 将区间 \([l + 1, j - 1]\) 赋值为 \(max{(a_{l \dots j})}\) 。最大化数组和。
题解:
\(n = 1\) 或 \(n = 2\) 时无法操作,直接求和即可。 否则除首尾外均可被置为 \(max{(a_{1 \dots n})}\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
void sol() {
int n, maxn = -1; cin >> n;
vector<int> a(n);
for (int i = 1; i <= n; i ++) cin >> a[i], maxn = max(maxn, a[i]);
cout << a[1] + a[n] + (n - 2) * maxn << endl;
}
signed main() {
int t; cin >> t;
while (t --) sol();
return 0;
}//C
K. Constructive
难度:1200
题意:
给定长度 \(n\) ,构造数组 \({a_n}\) 满足数组中的每个元素 \(a_i\) 都是正整数,所有元素的乘积等于所有元素的和,数组中的元素互不相同。
题解:
观察可知,除了 \(n = 1\) 和 \(n = 3\) 外不可能,证明可思考数列求和 \(\sum_{i = 1}^{n}n\) 和累乘 \(\prod_{i=1}^n n\) 的交点。
#include<bits/stdc++.h>
#define int long long
using namespace std;
void sol(){
int n; cin >> n;
if(n == 1) cout << "YES\n" << "1\n";
else if(n == 3) cout << "YES\n" << "1 2 3\n";
else cout << "NO\n";
}
signed main() {
int t = 1; cin >> t;
while (t --) sol();
return 0;
}//K
E. Block Game
难度:1200
题意:
给定一排数字方块和一个万能方块,每次可将万能方块插到最左并挤出最右,问经过任意次操作后,序列最左方块与最终万能方块之和的最大值。
题解:
把序列和万能方块合成一个长度为 \(n+1\) 的环:\(b=[a_1,a_2,\dots,a_n,k]\) 一次操作等价于把这个长度 \(n+1\) 的数组整体向右循环位移 \(1\) 位:新序列取前 \(n\) 个,新万能方块取最后 \(1\) 个。因此经过若干次操作后,最左方块 + 万能方块就是位移后的 \(b_1+b_{n+1}\)。而在环上,位置 \(1\) 与 \(n+1\) 永远是相邻的,所以我们能得到的所有结果,恰好对应于环上任意一对相邻元素之和。所以答案就是环上相邻和的最大值。
#include<bits/stdc++.h>
#define int long long
using namespace std;
void sol() {
int n, k, ans = -1e9; cin >> n >> k;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n - 1; i++) ans = max(ans, a[i] + a[i + 1]);
cout << max({ans,a[n - 1] + k, a[0] + k}) << endl;
}
signed main() {
int t = 1; cin >> t;
while (t --) sol();
return 0;
}//E
B. Card Game
难度:1450
题意:
小苯和小红各有 \(n\) 张牌,按规则依次比较最前面的牌,数大者得分并弃牌。小苯可以在开局前任意重排自己的牌,问有多少种重排方式能让他获得最大可能得分。
题解:
小红牌序列为 \(b_1\dots b_n\), \(m=\min(b_{1 \dots n})\)。
若小苯当前最前牌为 \(x\le m\),因为小红所有牌都 \(\ge m\ge x\),小苯永远不可能赢一轮。
小红会不断弃掉自己的牌直到没牌,游戏立刻结束,此时小苯之后再大的牌也来不及出场。因此为了拿到最高分,任何 \(\le m\) 的牌都必须放在所有可得分牌之后。
若小苯当前最前牌为 \(x>m\),无论当前小红最前牌是谁,小苯要么直接赢,要么他会反复输给小红,导致小红不断出新牌。
但因为牌堆里存在值为 \(m\) 的小红的牌且 \(m<x\),最终一定会遇到某个 \(b_j<m<x\) 使得小苯赢并弃掉这张 \(x\)。
并且由于所有可得分牌都 \(>m\),它们不可能在 \(b_j=m\) 时输掉,从而不会把 \(m\) 弃掉,保证后续每张 \(>m\) 的牌也都一定能被弃掉并得分。
因此小苯能得到的最高分就是 \(c\) 等于数组 \(a\) 中严格大于 \(m\) 的元素个数。
要达到该最高分,条件为所有 \(>m\) 的牌全部排在所有 \(\le m\) 的牌之前。
前 \(c\) 张任意排列、后面 \(n-c\) 张任意排列都可行,方案数为 \(c!(n-c)!\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int fact(int n){
int t = 1;
for (int i = 1; i <= n; i ++) t = t * i % 998244353;
return t;
}
void sol() {
int n, minn = 3e9, cnt = 0; cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++) {
int x; cin >> x; minn = min(minn, x);
}
for(int i = 0;i < n;i ++)
if (a[i] > minn) cnt++;
cout << fact(cnt) * fact(n - cnt) % 998244353 << endl;
}
signed main() {
int t = 1; cin >> t;
while (t --) sol();
return 0;
}//E
H. Blackboard
难度:1500
题意:
黑板上写着一个算式:\(a_1 + a_2 + \dots + a_n\) 共有 \(n-1\) 个加号。可以选择把其中一些加号擦掉横线,使其变成竖线 \(|\) 变为或运算。有多少种不同的擦黑板方式,使得新算式的计算结果与原算式相同。
题解:
可视为若干段按位或,随后相加。对于每一段或的结果一定小于等于求和的结果。想要不改变结果只能取等号,观察可知当这一段内任意两个数的二进制 \(1\) 不重叠(某个数字某位上为 \(1\),其余数字该位置均为 \(0\))时,可取等号。
问题转为,把数组划分成若干段,使得每一段内所有数的二进制 \(1\) 位互不冲突,统计方案。
简单来说可以使用双指针维护区间 \([l,r]\) 保证区间内按位或时无重复 \(1\)。
若加入新的尾巴 \(a[r]\) 后有重复,就不断右移 \(l\)。找到每个 \(r\) 的最小合法左边界。
接下来就可以 \(dp\),设 \(dp_i\) 为前 \(i\) 个方案数,转移方程就是: \(dp[i]=\sum^i_{j=l}dp[j−1]\)。
最后使用前缀和优化一下。
#include<bits/stdc++.h>
#define int int
using namespace std;
void sol() {
int n, mask = 0, l = 1; cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
vector<int> dp(n + 1), pre(n + 1);
pre[0] = 1;
for (int r = 1; r <= n; r++) {
while (mask & a[r]) //与运算可以判断是否重复
mask ^= a[l], l ++;//异或可以删除左边的元素
mask |= a[r];//添加右边的元素
if(l >= 2) dp[r] = pre[r - 1] - pre[l - 2];
else dp[r] = pre[r - 1];
if (dp[r] < 0) dp[r] += 998244353;
pre[r] = pre[r - 1] + dp[r];
if (pre[r] >= 998244353) pre[r] -= 998244353;
}
cout << dp[n] << '\n';
}
signed main() {
int t = 1; cin >> t;
while (t --) sol();
return 0;
}

浙公网安备 33010602011771号