2025 寒假集训补题

news/2025/2/23 23:52:06/文章来源:https://www.cnblogs.com/f2021ljh/p/18733123

ACM@HIT 2025 寒假集训

0109 简单数据结构

回放 | 题单

New Energy Vehicle(贪心,堆)

有一辆含 \(n\) 个电瓶的车,第 \(i\) 个电瓶的容量 \(a_i\),耗 \(1\) 单位任意电瓶的电力前进 \(1\)(只能向前),有 \(m\) 个充电站,第 \(j\) 个充电站位于 \(x_j\),可以给电瓶 \(t_j\) 充满电。求初始电瓶满的情况下最远可以行驶多远。

数据组数 \(1\le T\le10^4\)\(1\le n,m\le10^5\)\(\sum n,\sum m\le2\cdot10^5\)\(1\le a_i\le10^9\)\(1\le x_j,t_j\le10^9\),保证 \(x_i\) 两两不等,且按从小到大顺序给出。

有的电瓶可以充电,而有的电瓶不可以。为了最大化被利用的电力,我们应该优先使用可充电的电瓶,并且优先使用距离其充电站最近的电瓶。用堆(优先队列 std::priority_queue)维护这一贪心过程。

将充电站的编号作为元素加入堆,按位置从近到远排序。每种电瓶只需加入最近的充电站。

遍历所有充电站,假设当前要从充电站 \(i-1\) 到达充电站 \(i\),则当前要走的距离为 \(d=x_i-x_{i-1}\)(令 \(x_0=0\))。不断取堆顶的充电站对应的电瓶中的电力,直到到达 \(i\),即 \(d\) 减到 \(0\),或者直到堆中的电力已被用完,这时开始用不可充电的电瓶中的电力。如果过程中所有电力都被耗尽,则输出答案,否则 \(d\) 减到 \(0\),充电站 \(i\) 将电瓶 \(t_i\) 充满电,于是将 \(t_i\) 的下一个充电站加入堆,继续前往下一个充电站 \(i+1\)

最后,到达最后一个充电站 \(m\),则把所有电瓶中的剩余电量耗尽,结束行驶,输出答案。

#include <bits/stdc++.h>
#define int long long
#define f(x, y, z) for (int x = (y); x <= (z); ++x)
using namespace std;
int const N = 1e5 + 10;
int tt, n, m, a[N], c[N], nxt[N], sum;
struct node {int x, t;
} b[N];
vector<int> v[N];struct cmp {bool operator()(int const &p, int const &q) {return b[p].x > b[q].x;}
};
priority_queue<int, vector<int>, cmp> q;void solve() {sum = 0;f(i, 1, n) v[i].clear();f(i, 1, m) nxt[i] = 0;while (!q.empty()) q.pop();cin >> n >> m;f(i, 1, n) cin >> a[i], c[i] = a[i], sum += a[i]; // sum: 所有电池的总电量, 动态变化f(i, 1, m) cin >> b[i].x >> b[i].t, v[b[i].t].push_back(i);f(i, 1, n) {f(j, 0, (int)v[i].size() - 2) {nxt[v[i][j]] = v[i][j + 1]; // nxt[i]: 下一个与充电站 i 所充电瓶相同的充电站}if (v[i].size()) q.push(v[i][0]); // 将充电站编号加入堆}f(i, 1, m) {int dis = b[i].x - b[i - 1].x;int tp = 0;while (dis && !q.empty()) {tp = q.top(); q.pop();int tmp = min(c[b[tp].t], dis);c[b[tp].t] -= tmp, dis -= tmp, sum -= tmp;}if (dis == 0) { // 到达 isum += a[b[i].t] - c[b[i].t], c[b[i].t] = a[b[i].t];if (nxt[i]) q.push(nxt[i]); // 如果当前电池充完电后, 后面还有充电站能给它充电if (b[tp].t != b[i].t && c[b[tp].t] != 0) q.push(tp); // 如果最后用的电瓶中还有电, 将其放回堆中} else { // 只用能充电的电瓶, 没到达 iint tmp = min(sum, dis);sum -= tmp, dis -= tmp;if (dis == 0) { // 用不能充电的电瓶到达 isum += a[b[i].t] - c[b[i].t], c[b[i].t] = a[b[i].t];if (nxt[i]) q.push(nxt[i]);} else { // 电量耗尽, 结束行驶cout << b[i].x - dis << '\n';return;}}}cout << b[m].x + sum << '\n';return;
}signed main() {cin.tie(0)->sync_with_stdio(false);cin >> tt;while (tt--) solve();return 0;
}

Make Max(笛卡尔树)

给定长度为 \(n\) 的序列 \(a_1,\cdots,a_n\),对其进行操作如下:

  • 选定一个连续子序列 \(a_l,\cdots,a_r\)\(1\le l<r\le n\)),其中的数不都相同(即存在 \(i,j\in[l,r]\) 满足 \(i\ne j\)\(a_i\ne a_j\)),将其中所有数变成 \(\max_{l\le i\le r}\{a_i\}\)

问最多可以进行上述操作多少次。

数据组数 \(1\le t\le1000\)\(1\le n\le2\cdot10^5\)\(\sum n\le4\cdot10^5\)\(1\le a_i\le10^9\)

Valiant's New Map(二分答案,二维前缀和)

给定一个 \(n\times m\) 的矩阵 \(A=(a_{ij})\),找到最大的 \(l\),使得存在一个 \(l\times l\) 的子块,其中的元素都不小于 \(l\)

数据组数 \(1\le t\le10^3\)\(1\le n\le m\)\(1\le\sum(n\cdot m)\le10^6\)\(1\le a_{ij}\le10^6\)

二分答案,假设二分的答案为 \(x\),验证是否存在一个边长等于 \(x\) 的方子块,使得其中的元素都大于等于 \(x\)

为了快速判断以 \((i,j)\) 为左上角的边长为 \(x\) 的方子块,是否满足其中元素均大于等于 \(x\),建立辅助矩阵 \(B=(b_{ij})_{n\times m}\),其中 b[i][j] = a[i][j] >= x ? 0 : 1。那么如果 \(\sum\limits_{p=i}^{i+x}\sum\limits_{q=j}^{j+x}b_{pq}=0\),则说明以 \((i,j)\) 为左上角的边长为 \(x\) 的方子块满足要求,\(O(nm)\) 枚举即可。总复杂度 \(O(nm\log n)\)

inline int sum(const vector<vector<int> > & a, int x1, int y1, int x2, int y2) {return a[x2][y2] - a[x1 - 1][y2] - a[x2][y1 - 1] + a[x1 - 1][y1 - 1];
}bool check(int x, const vector<vector<int> > & a) {vector<vector<int> > b(n + 1, vector<int>(m + 1, 0));f(i, 1, n) f(j, 1, m) b[i][j] = (a[i][j] < x);f(i, 1, n) f(j, 1, m) b[i][j] = b[i][j] + b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];f(i, 1, n - x + 1) f(j, 1, m - x + 1)if (sum(b, i, j, i + x - 1, j + x - 1) == 0) return true;return false;
}void solve() {cin >> n >> m;vector<vector<int> > a(n + 1, vector<int>(m + 1, 0));f(i, 1, n) f(j, 1, m) cin >> a[i][j];int l = 0, r = n + 1, mid;while (l + 1 < r) {mid = l + r >> 1;if (check(mid, a)) l = mid;else r = mid;}cout << l << '\n';return;
}

Substract Operation(结论题,std::set

给定两个数 \(n,k\)\(n\) 个数 \(a_1,a_2,\cdots,a_n\)。一次操作是指,从数列中选出一个数 \(x\),将其删去,剩余的数分别减去 \(x\)。问能否通过 \(n-1\) 次操作,使最后剩下的一个数恰好为 \(k\)。如果可以,输出 YES(大小写不限)。

数据组数 \(1\le t\le10^4\)\(2\le n\le2\cdot10^5\)\(2\le\sum n\le2\cdot10^5\)\(-10^9\le a_i\le10^9\)\(1\le k\le10^9\)

顺序不重要。假设 \(n=4\),按顺序删去 \(a_1,a_2,a_3\),那么有:

\[(a_1,a_2,a_3,a_4)\to(a_2-a_1,a_3-a_1,a_4-a_1)\to(a_3-a_2,a_4-a_2)\to(a_4-a_3). \]

也就是说,最后剩下的一定为两个数的差。那么只需要检查是否存在两个数的差等于 \(k\) 即可。利用 std::set\(\forall i\) 检查数列中是否存在 \(a_i+k\) 即可。时间 \(O(n\log n)\)。由于 std::unordered_set 一次操作的最坏时间复杂度为 \(O(n)\),对于此题数据会超时。。。

void solve() {cin >> n >> k;set<int> s;vector<int> a;f(i, 1, n) {int x; cin >> x;a.push_back(x);s.insert(x);}for (int x: a) if (s.find(x + k) != s.end())return void(cout << "YES\n");cout << "NO\n";return;
}

Closest Pair(结论题)

给定 \(n\) 个二元组 \((x_i,w_i)\),其中 \(x_i\) 严格单调递增。给出 \(q\) 次询问,每次询问给出 \(l,r\)\(1\le l<r\le n\)),求

\[\min_{l\le i<j\le r}\{|x_i-x_j|\cdot(w_i+w_j)\}. \]

\(2\le n\le3\cdot10^5\)\(1\le q\le3\cdot10^5\)\(|x_i|\le10^9\)\(1\le w_i\le10^9\)

0110 简单 STL

回放 | 题单

关押罪犯(扩展域并查集)

\(N\) 名罪犯要被关押进两座监狱,其中有 \(M\) 对罪犯之间有仇,假设第 \(i\) 对有仇的罪犯为 \(\{a_i,b_i\}\),他们之间的怨气值为 \(c_i\),那么如果他们被关押在同一座监狱,则会爆发影响力为 \(c_i\) 的冲突事件。问如何分配罪犯到这两座监狱,使得冲突事件的影响力的最大值最小。

\(N\le2\cdot10^4\)\(M\le10^5\)\(0<c_i\le10^9\)\(1\le a_i<b_i\le N\)

根据贪心的思想,尽量将冲突影响力最大的罪犯放到不同的监狱。首先将冲突事件按影响力从大到小排序,然后将事件的双方试着分别放到不同的监狱。如果在此之前的事件已经将二者放到同一监狱,那么就只能以当前事件为影响力最大的事件了。

「放到监狱」的操作用扩展域并查集来实现。由于一个罪犯一定属于两个监狱中的一个,这符合「敌人的敌人是朋友」的思想。

#include <bits/stdc++.h>
#define f(x, y, z) for (int x = (y); x <= (z); ++x)
using namespace std;
int const N = 2e4 + 10, M = 1e5 + 10;
int n, m;int fa[N << 1];
int getfa(int x) { return x == fa[x] ? x : (fa[x] = getfa(fa[x])); }
void Merge(int x, int y) {int f1 = getfa(x), f2 = getfa(y);if (f1 ^ f2) fa[f1] = f2;return;
}struct Node {int a, b, c;inline bool operator<(Node const & o) const {return c > o.c;}
} s[M];signed main() {cin.tie(0)->sync_with_stdio(false);cin >> n >> m;f(i, 1, n) fa[i] = i, fa[i + n] = i + n;f(i, 1, m) cin >> s[i].a >> s[i].b >> s[i].c;sort(s + 1, s + m + 1);f(i, 1, m) {int f1 = getfa(s[i].a), f2 = getfa(s[i].b);if (f1 == f2) return cout << s[i].c << '\n', 0;else Merge(s[i].a, s[i].b + n), Merge(s[i].b, s[i].a + n);}cout << "0\n"; //没有冲突事件发生return 0;
}

黑匣子(对顶堆)

给定长度为 \(n\) 的元素序列 \(a_1,\cdots,a_n\) 和长度为 \(m\) 的下标序列 \(u_1,\cdots,u_m\)。另外有一个初始为空的集合,对于每个 \(1\le i\le m\),将 \(a_{u_{i-1}},\cdots,a_{u_i}\) 加入集合后(规定 \(u_0=1\)),输出集合中第 \(i\) 大的数(即集合中的元素从小到大排序后的第 \(i\) 个元素)。

\(1\le n,m\le2\cdot10^5\)\(|a_i|\le2\cdot10^9\)\(1\le u_1\le\dots\le u_m\le n\)

动态维护集合中第 \(i\) 大的数,采用对顶堆的方法实现。维护一个小根堆和一个大根堆,其中:

  • 小根堆中的元素 >= 小根堆的根 >= 大根堆的根 >= 大根堆中的元素。
  • 大根堆中有 \(i-1\) 个元素,两堆中一共有 \(u_i\) 个元素。这时,小根堆的根即为第 \(i\) 大的元素。

对于每个 \(1\le i\le m\),不断向两堆中加入元素,直到元素个数达到 \(u_i\)。先将元素加入大根堆,当大根堆中元素达到 \(i\) 时,将大根堆的堆顶放到小根堆中。

\(n,m\) 同阶,时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
using namespace std;
int const N = 2e5 + 10;
int n, m, a[N];priority_queue<int> qmx; // 大根堆
priority_queue<int, vector<int>, greater<int> > qmn; // 小根堆signed main() {cin.tie(0)->sync_with_stdio(false);cin >> n >> m;for (int i = 1; i <= n; ++i) cin >> a[i];for (int i = 1, t = 0, u; i <= m; ++i) {cin >> u;while (t < u) {qmx.push(a[++t]);if (qmx.size() == i) qmn.push(qmx.top()), qmx.pop(); // 一旦大根堆中达到 i 个元素, 就移到小根堆中一个, 并且保证小根堆的根 (min) >= 大根堆的根 (max)}cout << qmn.top() << '\n'; // 此时大根堆中有 i-1 个元素, 小根堆的堆顶是第 i 大的元素, 两个堆中共有 u 个元素qmx.push(qmn.top()), qmn.pop(); // 保证下一轮大根堆中有 (++i)-1 个元素}return 0;
}

0113 最小生成树

回放 | 题单

0114 最短路

回放 | 题单

0116 Tarjan

回放 | 题单

0117 倍增与 ST 表

回放 | 题单

0120 树状数组与线段树

回放 | 题单

0121 分块与莫队

回放 P1(密码:nn7n) | 回放 P2(密码:nn7n) | 题单

0122 最终测试

比赛链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/888631.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

区块链模型原理入门学习2——细化模型

以上描述中,存在一些非生产情景的理想化设定。比如:1.没有设计谜题难度平衡 2.没有加入加密校验数字签证 3.没有设计个人钱包 4.广播问题【敬畏能量 敬畏自然】

upload-labs/Pass-12 白名单检测 - %00 截断 GET

save_path 为客户端向服务器端传递的额外信息,可能用此参数指定上传文件的保存目录将save_path 更改为../upload/test.php%00 filename=test.png,filename 的后缀名需要满足白名单 路径和文件名组合在一起会变成../upload/test.php%00test.png , 后缀名满足白名单那么文件就…

upload-labs/Pass-13 白名单检测 - %00 截断 POST

POST 数据包的路径在请求体中在请求体中添加文件名称test.php, 但是不能像GET请求的数据包一样直接添加%00 了,我们需要直接在hex 中将标记修改为00;同时记得把文件名修改为可以上传的后缀重放数据包即可成功绕过上传

upload-labs/Pass-14 Pass-15 图片码绕过

copy .\test.png/b+.\test.php/a kb.png /b:以二进制模式 读取文件 .\test.png /b:以二进制读取test.png /a:以 ASCII 文本模式 读取文件,遇到第一个 EOF(文件结束符,如 0x1A)时停止读取。 .\as.php /a:以 ASCII 文本模式 读取as.php +:表示合并操作,将多个文件内容拼…

upload-labs/Pass-07 黑名单检测 -空格绕过

代码中没有对文件左右两侧去除空格,在文件名后面添加空格不影响文件执行,因此可以绕过

upload-labs/Pass-06 黑名单检测 - 后缀大小写绕过

利用Windows对大小写不敏感的特性。代码中没有对文件大小写做归一,更改文件后缀名大小写可以绕过黑名单

upload-labs/Pass-05 Pass-10 黑名单检测 - 点空格点绕过

利用Windows系统的文件名特性,会自动去掉后缀名最后的.,上传 as.php..进行绕过。 在Windows系统下命名test.php. . ; 命名成功后显示的文件名称为test.php在BP 中更改文件名,文件上传成功访问文件成功 3. 代码分析:删除文件名最后的点之后,以字符串最后的点为分界线,点之…

jvm调优_内存泄漏诊断

1️⃣ 使用Spring Boot模拟场景:注入List持续添加数据(内存泄漏源) 2️⃣ Arthas实时分析: heapdump生成 → MAT导入 → 定位Dominator Tree 3️⃣ 修复验证: 优化弱引用缓存 → GC后观察对象回收实例代码通过api接口不断访问增加元空间的内存 启动 arthas 查看内存或使用…

【.NET】调用本地 Deepseek 模型

本篇咱们来聊一聊怎么在 .NET 代码中使用本地部署的 Deepseek 语言模型。大伙伴们不必要紧张,很简单的,你不需要学习新知识,只要你知道 .NET 如何访问 HTTP 和 JSON 的序列化相关就够了。 先说说如何弄本地模型,有伙伴会问:直接用在线的不好?其实,本地部署更实用,也更符…

抽象类和接口的对比、及各自的使用场景--java进阶day02

1.区别2.各自的使用场景1.抽象类的使用场景 如图,有三个类,其中存在共性,我们就会写一个父类并抽取出共性的东西,但有的方法难免会描述不清,所以我们就将其写为了抽象方法,抽象方法又得存在于抽象类中,所以抽象类主要是对事物做抽象,有些事物说不清2.接口的使用场景假如…

JUC并发—11.线程池源码分析

大纲 1.线程池的优势和JUC提供的线程池 2.ThreadPoolExecutor和Excutors创建的线程池 3.如何设计一个线程池 4.ThreadPoolExecutor线程池的执行流程 5.ThreadPoolExecutor的源码分析 6.如何合理设置线程池参数 + 定制线程池1.线程池的优势和JUC提供的线程池 (1)为什么使用线程池…

Redis中的缓存穿透,缓存击穿和缓存雪崩

概述 缓存击穿、缓存穿透、缓存雪崩这三个问题是Reids在实际项目中会经常遇到问题,同时,这三个问题也是面试的热点问题,下面,就本篇文章搞懂缓存穿透、缓存击穿、缓存雪崩三大问题的原因及解决方法。 Redis在项目中作为缓存中间件是如何工作的?如图所示客户端发起一个查询…