尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

NOIP 模拟赛 3 比赛总结

NOIP 模拟赛 3 比赛总结
📅 发布时间:2026/6/20 8:43:51

分数:\(100 + 60 + 0 + 10 = 170\)

T4 最终结果没取模,挂了 60 分!永康喵喵又翻车了!

T1

一道不太水的水题。

这道题要求满足 \(1 \le i < j < k < l \le n\) 且 \(a_i \oplus a_j \oplus a_k \oplus a_l = 0\) 的四元组数量。很明显要用到异或的一个性质,即 \(\forall \ n \in \mathrm{\mathbf{Z}}, n \oplus n = 0\)。所以,我们可以先暴力计算出所有二元组的异或值,将它们存入桶(以下记为 \(b\))中,此时如果忽略特殊情况,答案就是 \(\sum\limits_{i=1}^{\max(a_i \oplus a_j)}\dfrac{(b_i-1)b_i}{2}\)。但是注意到可能会出现类似 \((x, x, y, z)\) 的错误四元组(由 \((x, y)\) 和 \((x, z)\) 两个二元组结合而成),这是 \(a_y = a_z\) 造成的,所以需要去重。对于每两个重复的数,会产生 \(n-2\) 个错误四元组。设 \(a\) 中每个数 \(i\) 出现的的次数为 \(c_i\),则总共需要去除 \(\left(\sum\limits_{i=1}^{max(a_i)}\dfrac{(c_i-1)c_i}{2}\right)(n-2)\) 个错误二元组。此外考虑四元组 \((i, j, k, l)\) 可能由三对二元组组合而成:\((i, j)\) 与 \((k, l)\)、\((i, k)\) 与 \((j, l)\),以及 \((i, l)\) 与 \((j, k)\),因此最终答案需要除以 \(3\)。

#include <bits/stdc++.h>
#define int long long
const int N = 5010;
int n, a[N];
int xorBuc[(int)1.5e6], numBuc[(int)1e6+10];
int ans;int calc(int x) {if (x == 0 || x == 1) return 0;x--;return (1 + x) * x / 2;
}signed main() {freopen("xor.in", "r", stdin);freopen("xor.out", "w", stdout);std::ios::sync_with_stdio(false); std::cin.tie(0);std::cin >> n;int numMx = 0;for (int i = 1; i <= n; i++) {std::cin >> a[i];numBuc[a[i]]++;numMx = std::max(numMx, a[i]);}int xorMx = 0;for (int i = 1; i <= n; i++) {for (int j = i + 1; j <= n; j++) {xorBuc[a[i] ^ a[j]]++;xorMx = std::max(xorMx, a[i] ^ a[j]);}}for (int i = 0; i <= xorMx; i++) {ans += calc(xorBuc[i]);}for (int i = 0; i <= numMx; i++) {ans -= calc(numBuc[i]) * (n - 2);}std::cout << ans / 3 << '\n';return 0;
}

T2

据机房里的大佬说这是一道非常简单的分层图题,可惜我并没有学过。

分层图,可以理解为把图复制成好几份,然后垂直叠放。在这些图中有一些通道,可以上下穿梭。仔细阅读题目,我们就会发现,汽车的速度看似很复杂,实际上只可能是 \(2\) 的倍数。考虑到在每条边上经过的时间需要向上取整,且边长最多为 \(10^6 \approx 2^{20}\),因此太快的速度是没有意义的,最多只需要进行 \(20\) 次加速,因此我们只需要建 \(21\) 层图。首先我们按照正常建图的流程,水平地把图建好,并把从下往上数第 \(i\) 层图的边权除以 \(2^i\),向上取整。然后开始搭建垂直穿梭的“电梯”:在每个维修站处,向上搭一条边权为 \(c_i\) 的边;除第一层外,把每一条坑洼路段的终点改接到第一层(可以在最开始建图时完成,这么叙述是为了便于理解)。然后按照正常方法跑 Dijkstra 即可。


使用画图 3D 绘制的简陋分层图图示。红色结点代表维修站,绿色边代表加速通道,紫色边代表坑洼路段。

#include <bits/stdc++.h>
#define int long long
typedef long long ll;
const int N = 5e6+10;
struct Edge {int to, len;
};
std::vector<Edge> g[N];
int n, m, p, s, t, dis[N]; bool vis[N];
int getCeil(int a, int b) {if (a % b == 0) return a / b;else return a / b + 1;
}
struct Node {int u, dis;bool operator > (const Node &a) const {return dis > a.dis;}
};
std::priority_queue<Node, std::vector<Node>, std::greater<Node> > q;signed main() {std::ios::sync_with_stdio(false); std::cin.tie(0);std::cin >> n >> m >> s >> t;for (int i = 1; i <= m; i++) {int u, v, w; char ch;std::cin >> u >> v >> w >> ch;g[u].push_back({v, w});if (ch == 'G') {for (int level = 1; level <= 20; level++) {g[n * level + u].push_back({n * level + v, getCeil(w, 1 << level)});}} else {for (int level = 1; level <= 20; level++) {g[n * level + u].push_back({v, getCeil(w, 1 << level)});}}}std::cin >> p;for (int i = 1; i <= p; i++) {int x, c;std::cin >> x >> c;for (int level = 0; level <= 19; level++) {g[n*level+x].push_back({n*(level+1)+x, c});}}for (int i = 1; i <= n * 21; i++) {dis[i] = 1e18;}dis[s] = 0;q.push({s, 0});while (!q.empty()) {auto u = q.top().u;q.pop();if (vis[u]) continue;vis[u] = true;for (auto &v : g[u]) {if (dis[u] + v.len < dis[v.to]) {dis[v.to] = dis[u] + v.len;q.push({v.to, dis[v.to]});}}}int ans = 1e18;for (int level = 0; level <= 20; level++) {ans = std::min(ans, dis[n * level + t]);}if (ans == 1e18) {std::cout << "-1\n";} else {std::cout << ans << '\n';}return 0;
}

如何判断何时应使用分层图?当在移动时,一些数据发生了变化(如速度、代价、油量)等,且变化的可能性不是很多(如本题的速度最多只有 \(21\) 种)时,很适合用分层图。

T3

这道题可以想到一个比较朴素的 DP 做法:设 \(f(i, j, k)\) 表示当前在 \((i, j)\),已经走了 \(k\) 步的方案数。很容易可以转移。

然而,这道题的 \(e\) 最大可达 \(10^9\),如果真的要转移这么多次的话,恐怕等我们 AFO 了,大样例也算不出来。然而,每一次的转移过程又是相同而枯燥的,就像是计算幂一样,把一个数一步一步地乘以本身。而幂是有快速幂算法的,那这道题能不能也利用类似的思想呢?

当然可以!不过需要亿点点前置知识。

1. 矩阵

矩阵这个名字听起来很高级,其实就是一个二维数组。我们通常用大写字母来表示矩阵,在其右下角标注长宽信息,如 \(A_{3 \times 4}\) 就是一个有三行四列的矩阵。

在表示矩阵的元素时,通常用一个中括号包裹。如

\[A_{3 \times 4} = \left[\begin{matrix}1 & 1 & 4 & 5\\1 & 4 & 1 & 9\\1 & 9 & 8 & 1\\\end{matrix} \right] \]

2. 矩阵乘法

矩阵和数一样,是可以进行加法和乘法运算的。这里我们只讨论乘法。

两个矩阵若要相乘,则必须满足第一个矩阵的列数等于第二个矩阵的行数。乘积矩阵第 \(i\) 行第 \(j\) 列的元素等于左矩阵第 \(i\) 行与右矩阵的第 \(j\) 列对应元素之和,即 \((AB)_{ij} = \sum\limits_{k=1}^{n} a_{ik} b_{ij}\)。

3. 矩阵快速幂

矩阵的幂的定义和整数的幂一模一样,都是重复乘以自身。两者快速幂的写法也非常类似(具体可以看稍后的代码)。

说了这么多,这道题和矩阵乘法究竟有什么关系呢?如果我们用一个包含布尔值的矩阵 \(B_{i, j}\) 来表示是否可以从状态 \(i\) 一步转移到状态 \(j\),初始状态为矩阵 \(A\),那么 \((AB^n)_{i, j}\) 就可以表示从状态 \(i\) 经过 \(n\) 步到达状态 \(j\) 的路径数。

在代码实现中,我们使用 struct 封装了一个矩阵,使得矩阵的运算更加方便快捷。JZ8 非常喜欢矩阵,因为他可封装性很好。

#include <bits/stdc++.h>
#define int long long
const int N = 110, MOD = 19260817;
int m;struct Matrix {int n, m, a[N][N];inline Matrix(int nn = 0, int mm = 0) {n = nn, m = mm;for (int i = 1; i <= nn; i++) {for (int j = 1; j <= mm; j++) {a[i][j] = 0;}}}inline void reset(int nn = 0, int mm = 0) {*this = Matrix(nn, mm); // 从 AeeE5x 大神那儿学来的神秘科技}inline Matrix operator*(const Matrix &w) const {Matrix ans(n, w.m);if (m != w.n)return ans;for (int i = 1; i <= n; i++) {for (int j = 1; j <= w.m; j++) {for (int k = 1; k <= m; k++) {ans.a[i][j] = (ans.a[i][j] + a[i][k] * w.a[k][j] % MOD) % MOD;}}}return ans;}inline void operator*=(const Matrix &w) {*this = *this * w; // 第一个星号是取内容,第二个星号是乘号,别被弄晕了}
} matrixA, matrixB; // matrixB[i][j] == 1 表示可以从状态 i 一步移动到状态 jsigned main() {std::ios::sync_with_stdio(false);std::cin.tie(0);std::cin >> m;while (m--) {int op, a, b, c, d, e;std::cin >> op >> a >> b >> c >> d >> e;matrixA.reset(1, a * b);matrixB.reset(a * b, a * b);if (op == 0) {for (int x = 1; x <= a * b; x++) { // 遍历第一个矩阵int xRow = (x - 1) / b + 1;      // 向上取整除法int xCol = (x % b == 0 ? b : x % b);for (int y = x + 1; y <= a * b; y++) {int yRow = (y - 1) / b + 1;int yCol = (y % b == 0 ? b : y % b);if (xRow == yRow ||xCol ==yCol /*车走直的移动方式,另外两种棋子同理,都是用判断条件表示移动方式*/)matrixB.a[x][y] = matrixB.a[y][x] = 1;}}} else if (op == 1) {for (int x = 1; x <= a * b; x++) {int xRow = (x - 1) / b + 1;int xCol = (x % b == 0 ? b : x % b);for (int y = x + 1; y <= a * b; y++) {int yRow = (y - 1) / b + 1;int yCol = (y % b == 0 ? b : y % b);if ((abs(xRow - yRow) == 2 && abs(xCol - yCol) == 1) ||(abs(xRow - yRow) == 1 && abs(xCol - yCol) == 2)) {matrixB.a[x][y] = matrixB.a[y][x] = 1;}}}} else {for (int x = 1; x <= a * b; x++) {int xRow = (x - 1) / b + 1;int xCol = (x % b == 0 ? b : x % b);for (int y = x + 1; y <= a * b; y++) {int yRow = (y - 1) / b + 1;int yCol = (y % b == 0 ? b : y % b);if (abs(xRow - yRow) == 2 && abs(xCol - yCol) == 2) {matrixB.a[x][y] = matrixB.a[y][x] = 1;}}}}matrixA.a[1][(c - 1) * b + d] = 1;while (e) {if (e & 1)matrixA *= matrixB;matrixB *= matrixB;e /= 2;} // 矩阵快速幂int ans = 0;for (int i = 1; i <= a * b; i++) {ans = (ans + matrixA.a[1][i]) % MOD;}std::cout << ans << '\n';}return 0;
}

T4

这道题有点过于超出我的能力范围了,先不补了。不过有一个教训:需要取模的题要在过程和结果中都取模,切忌忘了任何一个,不然你就会像我一样挂掉宝贵的 60 pts 并掉 1 点 rating。

相关新闻

  • 2025年云南GEO优化公司权威推荐榜单:seo优化/网站seo优化/百家号源头公司精选
  • ORACLE游标序列化
  • iqoo手机关掉视频彩铃

最新新闻

  • 2026 武汉本地正规瓷砖空鼓维修服务商盘点|无损免拆砖修复,全域上门售后有保障 - 宅安选房屋修缮
  • 动态主题建模中的异常值识别与前瞻信号分析
  • Qwen2.5-VL工业多模态微调实战:特殊行业数据适配指南
  • STM32 串口DMA+IDLE中断实战:高效数据帧接收与协议解析
  • 术语俗话 --- 驱动/固件/软件
  • 中原卖黄金避坑要点,实体店资质辨别教程合扬全程公开鉴价 - 奢侈品交易观察员

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号