2026牛客寒假算法基础集训营1 题解

L. Need Zero

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

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

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

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

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

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;
}
posted @ 2026-02-03 18:35  Li_Albert  阅读(38)  评论(0)    收藏  举报