2026 ICPC 武汉邀请赛
点击查看题面
Problem C 相信着你
题目大意
在游戏对决中,玩家已知未来
具体规则如下:
卡牌类型:
打击:造成
点伤害; 防御:提供
点格挡值。 每个回合中:
回合开始时,清空上手手牌及上一回合剩余的格挡值;
玩家抽到
张打击,以及 张防御; 玩家最多可打出
张手牌,每张牌至多打出一次; 怪物受到伤害累计达到或超过
时立即死亡,玩家获胜且游戏结束; 若怪物未死亡,怪物发动攻击,造成
点伤害。若 严格大于玩家本回合获得的格挡值,玩家死亡。
要求判定玩家能否在 "Yes" 及最早获胜的回合数;若不能击败或中途死亡,输出 "No"。
数据范围:
思路
本题是一道具有阶段独立性的贪心模拟题。
由于手牌和格挡值在每回合开始时均会被重置,回合与回合之间的唯一关联量即为怪物的剩余生命值
具体决策逻辑如下:
- 斩杀优先级最高(直接获胜)
- 游戏行动顺序中,玩家行动先于怪物。若玩家在当前回合全额出攻击牌能够直接清空怪物血量,则怪物根本没有机会发动攻击。
- 本回合最多可打出的打击牌数量为
。 - 若
,则本回合可直接取得胜利,输出 "Yes"与当前回合编号,提前终止程序。
- 生存底线判定
- 若无法在本回合击败怪物,则必须防御怪物的攻击
。 - 抵挡
点伤害至少需要打出的防御牌张数为:
- 由于每回合最多只能打出
张牌,且手牌中防御牌的总数仅为 ,因此若出现:
则说明当前手牌无法防住该次攻击,玩家必定死亡,直接输出 "No" 并退出。
- 贪心输出最大化
- 在确保打出
张防御牌后,本回合剩余可用的出牌槽位为 。 - 为尽快击败敌人,剩余槽位应全部尽可能用于打出打击牌,打出数量为:
- 扣减怪物的生命值:
。
若经历完完整的 "No"。
复杂度分析
时间复杂度:
空间复杂度:
参考代码
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void fc() {
int n, s, d, hp;
std::cin >> n >> s >> d >> hp;
for (int i = 1; i <= n; i++) {
int a, k;
std::cin >> a >> k;
int b = 5 - a;
int a_mx = std::min(a, 3);
if (a_mx * s >= hp) {
std::cout << "Yes\n" << i;
return;
}
int b_need = (k + d - 1) / d;
if (b_need > 3 || b_need > b) {
std::cout << "No\n";
return;
}
int cnt = std::min(a, 3 - b_need);
hp -= cnt * s;
}
std::cout << "No\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
// std::cin >> t;
while (t--) fc();
return 0;
}Problem H 矩形切割
题目大意
在第一象限内有一个左下角为
- 纵向切割 (
):给定正整数 ,沿直线 切割; - 横向切割 (
):给定正整数 ,沿直线 切割。
每次切割后,均会产生若干更小的矩形。要求在每次切割完成后,实时输出当前所有小矩形中面积的最大值。保证不会在同一位置进行重复切割。
数据范围:
- 对于
, - 对于
,
思路
本题是一道经典的一维解耦与动态有序集合维护问题。
1. 维度独立性分析
由于每一次横向切割或纵向切割都是完全贯通整个矩形平面的:
- 任何纵向切割
仅会将水平方向的某一段长度划分为两半,对垂直方向的所有线段长度没有任何影响; - 任何横向切割
仅会将垂直方向的某一段长度划分为两半,对水平方向的所有线段长度没有任何影响。
经过若干次切割后,网格状划分产生的所有子矩形,其面积均可表示为:
其中
因此,原二维问题可以直接解耦为两个完全独立的一维动态区间分裂与最大值维护问题。
2. 区间维护与更新机制
针对单一维度(以长度为
- 初始时,仅有一个完整的连续线段
,其长度为 。 - 当在位置
处进行切割时:
- 定位区间:在已有的不相交区间集合中,找到包含切割点
的区间 。利用有序映射 std::map<ll, pair<ll, ll>>(以左端点为键),通过 prev(mp.upper_bound(k))可在时间内定位。 - 删除旧区间:将原区间的长度
从维护全局长度集合的 std::multiset<ll>中移除(注意需使用迭代器删除单次出现)。 - 分裂与插入:将
分裂为两段连续新区间 与 ,长度分别为 与 。将分裂出的两个新区间信息写回 map,并将新长度插入 multiset。 - 查询最大值:
multiset内部自动保持升序,其逆向迭代器*s.rbegin()即为当前维度的最大分段长度。
复杂度分析
时间复杂度:
- 初始化单次建表开销为
。 - 每次切割操作中,包含一次
map的二分查找、一次原区间移除,以及两次新区间与长度的插入操作,涉及的平衡树/红黑树操作均为单次。 次操作的总时间复杂度为 。在 的规模下,整体操作次数在可接受的计算阈值内。
空间复杂度:
- 随着切割推进,两个维度维护的区间总数不超过
个。 map与multiset中存储的节点数与切割次数呈线性关系,总空间复杂度为。
参考代码
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int n, m, q;
struct st {
std::map<ll, std::pair<ll, ll>> mp;
std::multiset<ll> s;
void init(int n) {
mp[1] = {n, n};
s.insert(n);
}
void modify(int k) {
auto it = std::prev(mp.upper_bound(k));
ll l = it->first;
auto [r, w] = it->second;
s.erase(s.find(w));
ll x = k - l + 1, y = r - k;
mp[l] = {k, x};
mp[k + 1] = {r, y};
s.insert(x);
s.insert(y);
}
ll get() { return *s.rbegin(); }
};
void fc() {
std::cin >> n >> m >> q;
st l, r;
l.init(n);
r.init(m);
while (q--) {
int op, k;
std::cin >> op >> k;
if (op == 1) {
l.modify(k);
} else {
r.modify(k);
}
std::cout << (ll)l.get() * r.get() << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
// std::cin>>t;
while (t--) fc();
return 0;
}