26牛客暑假多校8
点击查看题面
I题 Bridge VI
题目大意
有
参赛者
- 最少有多少名参赛者的最终得分严格大于大豆的最终得分。
- 最多有多少名参赛者的最终得分严格大于大豆的最终得分。
数据范围:
,且
思路
假设大豆(参赛者
1. 计算最少严格大于大豆的人数 (最佳排名)
要让超过大豆的人数最少,应尽可能提高大豆的分数,故取
- 大豆最终得分
。 - 对手(参赛者
)最终得分 。若 ,则贡献 人,否则贡献 人。 - 对于其余
场比赛(假设两选手初始分分别为 ),每场增加的分数和为 : - 贡献
人:若 且 ,无论怎么分配 分,两人最终得分均严格大于 。 - 贡献
人:若 且 ,且满足分配后两人得分均 ,即存在 使 。这要求 。 - 贡献
人:其余情况,由于至少有一个人初始分 ,可将全部 分分配给另一人,保证初始分 者最终得分仍 ,故可控制增加人数为 。
2. 计算最多严格大于大豆的人数 (最差排名)
要让超过大豆的人数最多,应尽可能降低大豆的分数,故取
- 大豆最终得分
。 - 对手(参赛者
)最终得分 。若 ,则贡献 人,否则贡献 人。 - 对于其余
场比赛(假设两选手初始分分别为 ): - 贡献
人:若能使两人最终得分均严格大于 ,即存在 使 且 。因为得分可为实数,这等价于所需额外分数的严格下界和小于 ,即 。 - 贡献
人:若无法做到两个人均超过 ,但将所有 分给其中一人可使其超过 (即 或 )。 - 贡献
人:即使把 分全给某一人也无法超过 。
复杂度分析
时间复杂度:
参考代码
参考代码
#include <iostream>
#include <algorithm>
using namespace std;
using ll = long long;
void solve() {
int n;
ll m;
cin >> n >> m;
ll a0, a1;
cin >> a0 >> a1;
// q: 大豆能拿到的最大最终得分(用于计算最少超越人数)
// p: 大豆能拿到的最小最终得分(用于计算最多超越人数)
ll q = a0 + m, p = a0;
ll x_ans = (a1 > q ? 1 : 0);
ll y_ans = (a1 + m > p ? 1 : 0);
for (int i = 1; i < n; i++) {
ll u, v;
cin >> u >> v;
// 1. 评估极小化超越人数贡献
if (u > q && v > q) {
x_ans += 2;
} else if (u > q || v > q || u + v + m > 2 * q) {
x_ans += 1;
}
// 2. 评估极大化超越人数贡献
ll req = max(0LL, p - u) + max(0LL, p - v);
if (m > req) {
y_ans += 2;
} else if (u + m > p || v + m > p) {
y_ans += 1;
}
}
cout << x_ans << ' ' << y_ans << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
if (cin >> t) {
while (t--) {
solve();
}
}
return 0;
}G题 Multiplication
题目大意
在十进制表示下,给定三个正整数
与 的长度均至少为 ,且前 位完全相同。 与 的长度均至少为 ,且前 位完全相同。 - 乘积
与 的长度均至少为 ,但两者的前 位不相同。
数据范围:
思路
为了使
根据
1. 当 时
- 令
, (长度为 )。 - 令
, (长度为 )。
正确性验证:
与 完全相同,前 位显然相同。 长度为 (为 ), 长度为 (为 ),前 位均为 ,完全相同。 - 计算乘积:
其首位数字为
由于
首位为 , 首位为 ,从第 位起即不相同,故对任意 ,前 位均不相同。
2. 当 时
对称地将低位扰动施加在
- 令
, 。 - 令
, 。 同理可得 首位为 , 首位为 ,满足要求。
复杂度分析
时间复杂度:
空间复杂度:
参考代码
参考代码
#include <iostream>
#include <string>
using namespace std;
void solve() {
int a, b, c;
cin >> a >> b >> c;
// 根据 a 和 b 的大小关系分情况构造,确保进位效果成立
if (a >= b) {
// x1 = a个9, y1 = '1' + (b-1)个0
// x2 = a个9, y2 = '1' + (b-1)个0 + '9'
cout << string(a, '9') << " " << '1' + string(b - 1, '0') << " ";
cout << string(a, '9') << " " << '1' + string(b - 1, '0') + '9' << "\n";
} else {
// x1 = '1' + (a-1)个0, y1 = b个9
// x2 = '1' + (a-1)个0 + '9', y2 = b个9
cout << '1' + string(a - 1, '0') << " " << string(b, '9') << " ";
cout << '1' + string(a - 1, '0') + '9' << " " << string(b, '9') << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--) {
solve();
}
return 0;
}B题 Deep Finesse
题目大意
汐与风子在包含
每轮由风子先手打出一个数字,汐必须用一个尚未打出的手牌回应:
- 若汐的回应数字
风子的数字,该轮安全度过。 - 若汐的回应数字
风子的数字,风子立即获胜。
汐已知手牌中必须包含给定的
数据范围:
- 测试用例数
。 ,且 。 ,且 中无重复元素。
思路
- 充要条件推导:
- 风子为了获胜,最优策略是按从小到大的顺序打出自己手上的牌。
- 设汐在
中的手牌数量为 。风子在该区间内的牌数为 。 - 若存在某个
使得 (即 ),风子只需依次打出这 张牌,汐手中能回应的牌至多只有 张,必定会被逼出 的牌而输掉比赛。 - 因此,必胜的充要条件为:对任意
,均有 。 - 当
时, ,意味着汐的所有手牌必须全分布于 中。若给定的指定数字中存在 ,则必胜方案数为 。
- 状态定义与转移方程:
- 设
表示在考虑前 个数字时,汐已选择 个数字作为手牌的方案数。 - 遍历
从 到 : - 当前选取数量的合法区间为
,其中 , 。 - 若
为必选数字(在给定的集合中),必须选择该数字,转移方程为:
- 若
为非必选数字,可选择或不选择,转移方程为:
- 对于
的状态,强制置 。
- 目标结果:
- 最终
即为满足前缀限制且包含所有指定数字的有效手牌方案总数。
复杂度分析
时间复杂度:对于每组测试用例,遍历长度为
空间复杂度:由于采用了倒序更新的一维滚动 DP 数组,空间复杂度为
参考代码
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll mod = 998244353;
void solve() {
int n, m;
cin >> n >> m;
bool valid = true;
vector<bool> forced((n << 1) + 1, false);
for (int i = 0; i < m; i++) {
int x;
cin >> x;
if (x >= (n << 1)) {
valid = false;
} else {
forced[x] = true;
}
}
if (!valid) {
cout << "0\n";
return;
}
vector<int> dp(n + 1, 0);
dp[0] = 1;
for (int i = 1; i < (n << 1); i++) {
int mn = (i + 1) >> 1;
int mx = min(i, n);
for (int j = mx; j >= mn; j--) {
if (forced[i]) {
dp[j] = dp[j - 1];
} else {
dp[j] = (dp[j] + dp[j - 1]) % mod;
}
}
for (int j = 0; j < mn; j++) {
dp[j] = 0;
}
}
cout << dp[n] << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}H题 It’s Magic, Not a Trick!
题目大意
梦野秘密子面前有
- 灌注:选择任意一张符咒,使其能量
。 - 释放:选择任意一张能量
的符咒,使其能量 。
约束条件:若灌注后没有任何符咒的能量达到或超过
求在保证每次施法均成功的前提下,所有符咒最终剩余能量之和的最小值,答案对
数据范围:
(测试用例数)
思路
- 等价转化与净收益分析: 单次成功的施法需要注入
单位能量,并扣除 单位能量,因此总能量净减少 。若 ,则每次操作总能量不发生改变,答案即为 。当 时,目标是尽可能多地执行合法施法。 - 单单元基础释放次数计算: 对于第
张符咒,在其自身 单位能量的基础上,考虑到触发释放前需要先执行一次 的灌注,该符咒独自能够支持的最大释放次数为:
在完成
- 自由能量池 (
) 的构建: 所有符咒在完成 次基础释放后,系统剩余的总能量为 。 其中,必须留存在各个符咒内部、无法自由挪用的静态能量和为 。 因此,能够被动态调配用于“补齐缺口”的自由能量池大小为:
- 贪心补齐策略: 若要让第
张符咒额外再触发一次释放,除了利用下一次施法自带的 点灌注外,还需要从自由能量池中补充的能量缺口为:
为了用有限的
- 大数处理与取模: 由于
,累加与乘法过程中需使用 __int128避免溢出,最终将结果对取模输出。
复杂度分析
- 时间复杂度:数据输入与基础状态计算为
,对补齐成本数组 进行排序耗时 。多组测试用例下,总时间复杂度为 ,完全符合 的时间限制。 - 空间复杂度:需要结构化数组存储
,空间复杂度为 ,符合 的内存限制。
参考代码
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void fc() {
ll n, x;
std::cin >> n >> x;
std::vector<ll> l(n), c(n), a(n);
__int128 sa = 0, sl = 0;
ll sc = 0;
for (int i = 0; i < n; i++) {
std::cin >> a[i];
sa += a[i];
// l[i]: 结合本次施法自带的 1 点灌注,当前符咒独立可支持的最大释放次数
l[i] = (a[i] + 1) / x;
// c[i]: 完成 l[i] 次释放后剩余的保留能量
c[i] = std::max(0LL, a[i] - l[i] * x);
sl += l[i];
sc += c[i];
// 重新定义 c[i] 为额外再触发一次释放所需的补齐能量缺口
c[i] = x - c[i] - 1;
}
// ans: 完成所有基础释放后的系统剩余总能量
// pool: 扣除不可自由调配的保留能量后,可用于补齐缺口的自由能量池
__int128 ans = sa - (x - 1) * sl, pool = ans - sc;
// 贪心策略:优先补齐缺口最小的符咒,以获得额外的释放次数
std::sort(c.begin(), c.end());
for (int i = 0; i < c.size(); i++) {
if (pool < c[i]) break;
pool -= c[i];
ans -= x - 1;
}
// 剩余自由能量池若大于等于 (x - 1),可继续进行组合释放
if (x > 1) {
ans -= pool / (x - 1) * (x - 1);
}
ll s = ans % 998244353;
std::cout << s << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
std::cin >> t;
while (t--) fc();
return 0;
}