当前位置: 首页 > news >正文

3 2025 04 23 模拟赛总结


成绩表

![[12 题解/photo/Pasted image 20250423183537.png]]


做题情况

  • T1:看了十分钟没什么思路,后来打表找到了一点规律,但是没写对(可能是因为细节太多)0pts
  • T2:这个题看起来唬人,实际不难,想了大概20分钟,后来写出来了,100pts
  • T3:这题是个三叉线段树,赛时没有想出来,直接打的暴力,25pts
  • T4:赛时没有时间写,0pts

赛时心态

心态比较平稳,T1用时过长,有些慌了神,T3的解法没有遇到过,估计再来点时间我还是会放弃。T4现在看来并不算难题,赛时应该多花点时间去思考,有可能做出来。


题面及题解

T1

传送门

题目描述

定义 \(f(x)=x\oplus (x+2^k)\),其中 \(\oplus\) 是二进制下的异或运算。

给定两个整数 \(n,k\)
请你输出 \(f(0)+f(1)+f(2)+\cdots+f(n)\) 的值。

【数据范围】
对于 \(100\%\) 数据,\(1\le T \leq 10^5\)\(0\le n < 2^{29}\)\(0\le k \leq 29\)


题解

这题的难点在于如何快速求出\(\displaystyle f(x)=x\,\oplus\,(x+2^{k})\)

我们考虑对于每个\(\displaystyle x + 2^{k}\)

  • \(\displaystyle x\)的第\(\displaystyle k\)位为0
    \(x+2^{k}\)相当于把\(x\)的第\(k\)位变成1,此时\(f(x)=2^{k}\)

  • \(x\)的第\(k\)位为1
    \(x+2^{k}\)会导致从第\(k\)位向左发生进位,此时的\(f(x)=2^{l}\)\(l\)为从第\(k\)位开始向左数连续1的个数,就像这样
    ![[12 题解/photo/Pasted image 20250423192448.png]]

所以我们可以去枚举连续1的长度,然后用 可能的情况数\(*\)这种情况的贡献即可求得答案


下面详细说说如何求得每种情况的情况数

设我们固定的1的长度为\(t\),注意:向左一位应该是0,否则不符合条件
首先,对于每个情况(也就是从第\(k\)位开始固定的1的长度),我们需要求出这个条件下\(\leq n\)的最大值\(p\),我们可以先将\(k\sim k+t\)位的一个0和\(t\)个1固定在相应的位置,然后枚举每一位,如果可以放入(不在\(k\sim k+t\)范围内且\(+p\)后的值\(\leq n\)),更新\(p\),这样就能够求出满足条件的最大的\(p\)

我们设p二进制中\(一个0+t个1\)的部分为\(x\),位于其前的部分为\(a\),其后的部分为\(b\),设当前有贡献的数为\(a'+x'+b'\)
而后我们分两种情况统计符合条件的情况

  • \(a'<a\)时,\(a'\)部分的贡献为\(a+1-1\),其中要考虑0和\(=a\)的情况
    \(b'\)则可以取任意数,共有\(k-1+1\)位,其中\(+1\)是因为有第0位,共有\(2^{k}\)种情况
    根据乘法原理,这种情况的贡献为\(a\times 2^{k}\)

  • \(a'=a\)时,\(a'\)部分的贡献为\(1\)\(b'\)部分只能取\(\leq b\)的数,因此\(b'\)部分的贡献为\(b+1\)

两种情况综合起来,再统计到答案即可。


code

using namespace std;
typedef long long ll;int t;
int n, k;int main() {scanf("%d", &t);while(t--) {scanf("%d%d", &n, &k);ll sum = 0;for(int i = 0; i <= 30; i++) {ll p = (1 << i) - 1;p = p << k;if(p > n) continue;for(int j = 30; j >= 0; j--) {if(j >= k && j <= k + i) continue;if(p + (1 << j) <= n) {p += (1 << j);}}int cnt1 = (p >> (k + i + 1)) << k;int cnt2 = p % (1 << k) + 1;sum += (ll)(cnt1 + cnt2) * (((1 << (i + 1)) - 1) << k);}printf("%lld\n", sum);}return 0;
}

T2

传送门

题目描述

在一个无限大的棋盘上有 \(n\)位置互不相同的棋子 \((x_i,y_i)\),你需要通过进行若干次以下操作删除全部的棋子:

  1. 选择一个格子 \((x,y)\)

  2. \((x,y)\) 上有棋子,则把这个棋子删掉,否则结束当前操作。

  3. 依次检查坐标为 \((x+1,y+1)\)\((x+1,y)\)\((x+1,y-1)\) 的格子上是否有棋子。当检查到第一个有棋子的格子时,停止检查,并将当前的 \((x,y)\) 更新为该格子的坐标后返回第二步。如果这三个格子都没有棋子,结束当前操作。

你要回答,最少操作多少次能把所有棋子删光。

【数据范围】
对于所有的测试数据,满足 \(1\le n\le 10^6\)\(1\le x_i,y_i\le 10^6\)


题解

这题较简单,我们画个图即可大概想到怎么做,所以画个图
![[12 题解/photo/Pasted image 20250423201110.png|600]]
对于每个节点,我们都可以走向下方的三个点,所以我们可以从上到下,从左到右枚举每个点,如果有可以到达的点,则将ans--(ans开始=n)

说一下赛后我有些疑惑的一个地方,如下图
![[12 题解/photo/Pasted image 20250423202503.png|500]]
事实上,我们选蓝色路线和红色路线是等价的,答案都是2,所以我们选择哪个点向下走与这个点能够向下走的深度无关。


code

using namespace std;
#define pii pair<int, int>
const int N = 1e6 + 10;int n;
map <pii, int> m;struct node {int a, b;int fa, fb;
}c[N];bool cmp(node x, node y) {if(x.a == y.a) {return x.b < y.b;}return x.a < y.a;
}int main() {scanf("%d", &n);int ans = n;for(int i = 1; i <= n; i++) {int x, y;scanf("%d%d", &x, &y);c[i].a = c[i].fa = x;c[i].b = c[i].fb = y;}sort(c + 1, c + 1 + n, cmp);for(int i = 1; i <= n; i++) {m[{c[i].a, c[i].b}] = i;}for(int i = 1; i <= n; i++) {for(int tt = -1; tt <= 1; tt++) {if(m.count({c[i].a + 1, c[i].b + tt})) {int id = m[{c[i].a + 1, c[i].b + tt}];if(c[id].fa == c[id].a && c[id].fb == c[id].b) {c[id].fa = c[i].a;c[id].fb = c[i].b;
//					printf("fa: %d  fb: %d  sa: %d  sb: %d\n", 
//					c[i].a, c[i].b, c[id].a, c[id].b);ans--;break;}}}}printf("%d\n", ans);return 0;
}

T3

传送门

题目描述

给定一个长度为\(3^{n}\)的由A和B组成的字符串\(s\)
\(q\)次询问,每次询问给定一个\(pos\),如果\(s[pos]==A\)则将\(s[pos]\)改为\(B\),反之亦然
每次询问,我们需要执行以下操作,直至字符串的长度为1

  • \(s[i]\)变为\(s[3i],s[3i-1],s[3i-2]\)中出现次数较多的字符,字符串的长度变为原来的三分之一

输出最后的字符

这个题乍一看没有什么思路,实际上并不难。由题可知,每个变化后的字符都是由三个连续的字符变化而来,并且还有修改操作,所以我们可以用三叉线段树来维护,然后这题就能被我们轻而易举的解决了。


code

const int N = 6e5 + 10;int n, m, cnt = 0;
char c[N];struct node {int dat, s[3];
}t[N << 2];void update(int p) {int ca = 0, cb = 0;for(int i = 0; i < 3; i++) {if(t[t[p].s[i]].dat) ca++;else cb++;}t[p].dat = ca > cb ? 1 : 0;return;
}void build(int &p, int l, int r) {p = ++cnt;if(l == r) {t[p].dat = c[l] == 'A' ? 1 : 0;return;}int mid1 = l + (r - l + 1) / 3 - 1;int mid2 = r - (r - l + 1) / 3;build(t[p].s[0], l, mid1);build(t[p].s[1], mid1 + 1, mid2);build(t[p].s[2], mid2 + 1, r);update(p);
}void modify(int p, int l, int r, int pos) {if(l == r) {t[p].dat = t[p].dat == 1 ? 0 : 1;return;}int mid1 = l + (r - l + 1) / 3 - 1;int mid2 = r - (r - l + 1) / 3;if(pos <= mid1) {modify(t[p].s[0], l, mid1, pos);} else if(pos <= mid2) {modify(t[p].s[1], mid1 + 1, mid2, pos);} else modify(t[p].s[2], mid2 + 1, r, pos);update(p);
} int main() {scanf("%d%d", &n, &m);scanf("%s", c + 1);int root = 0;int tt = pow(3, n);build(root, 1, tt);for(int i = 1; i <= m; i++) {int pos;scanf("%d", &pos);modify(root, 1, tt, pos);printf("%c\n", t[root].dat == 1 ? 'A' : 'B');}return 0;
}

终于到最后一题了,好累…… (T _ T)

T4

传送门

题目描述

给定一个 \(n\)\(m\) 边的无向图,第 \(i\) 条道路连接了 \(u_i\)\(v_i\),边权为 \(w_i\),第 \(i\) 个点的点权为 \(c_i\)

给定 \(q\) 组询问,第 \(i\) 组询问求从 \(s_i\)\(t_i\) 的路径的边权之和与点权的最大值的和的最小值。

可能有重边,但保证无自环。
对于 \(100\%\) 的数据,\(1 \le n \le 250\)\(1 \le m \le 10^4\)\(1 \le q \le 10^4\)


题解

这题比赛的时候没有时间正经想,再加上一大坨英文糊脸,直接放弃了。
做了以后发现并不是很难。

边权和的最小值很容易想到可以用Floyd,但是点权的最大值并不好确定并转移
为了解决这个问题,我们可以先将点按照点权从小到大排序,而后按照此顺序枚举中间点,这样点权的最大值即为\(max(max(c[i].val,c[j].val),c[k].val)\)

这样即可\(O(n^{3})\)预处理答案,通过此题


code

const int N = 255;int n, m, q;
int d[N][N];
int ans[N][N];struct node {int val;int id;
}c[N];bool cmp(node a, node b) {return a.val < b.val;
}int main() {memset(d, 0x3f, sizeof d);memset(ans, 0x3f, sizeof ans);scanf("%d%d%d", &n, &m, &q);for(int i = 1; i <= n; i++) {scanf("%d", &c[i].val);c[i].id = i;}for(int i = 1; i <= m; i++) {int x, y, z;scanf("%d%d%d", &x, &y, &z);d[x][y] = min(d[x][y], z);d[y][x] = min(d[y][x], z);}sort(c + 1, c + 1 + n, cmp);for(int k = 1; k <= n; k++) {for(int i = 1; i <= n; i++) {for(int j = 1; j <= n; j++) {d[c[i].id][c[j].id] = min(d[c[i].id][c[j].id], d[c[i].id][c[k].id] + d[c[k].id][c[j].id]);ans[c[i].id][c[j].id] = min(ans[c[i].id][c[j].id],d[c[i].id][c[j].id] + max(max(c[i].val, c[j].val),c[k].val));}}}while(q--) {int x, y;scanf("%d%d", &x, &y);printf("%d\n", ans[x][y]);}return 0;
}

完结撒花(w)

http://www.hskmm.com/?act=detail&tid=25007

相关文章:

  • 14 收心赛3 T1 最长不降子序列 题解
  • 16 LCA模拟赛1T1 密码 题解
  • 吴恩达深度学习课程一:神经网络和深度学习 第二周:神经网络基础(一)
  • 阿里开源规则引擎QLExpress
  • QOJ7411 Bitwise Xor
  • 完整教程:SOC-ESP32S3部分:25-HTTP请求
  • 为什么要采用“接口 - 抽象类 - 实现类”这种三层结构? - 浪矢
  • 对外提供 AI 服务的风险:合规视角与 AI 安全围栏落地指南
  • VScode C/C++ 汉化 竞赛版 只需下载扩展 (超简单)
  • 网络安全工具与社区讨论月报
  • 机器人运动未来与人机交互研究
  • 欧拉路径 欧拉图 小记
  • OI 笑传 #16
  • cf296b
  • 第一次使用Ttpora
  • Apache反向代理
  • 原版 Sunshine+虚拟显示器实现熄屏串流
  • 2025国庆Day4
  • gis坐标计算
  • day17 课程()
  • NKOJ全TJ计划——NP11744
  • ROIR 2025
  • trick 小记
  • python编写AI生常用匡架及使用指令集
  • 123123
  • 1005模拟赛总结
  • 2025.10.5 2024CCPC郑州
  • 20250531MATLAB三维绘图 - 教程
  • 概率期望dp 复习笔记
  • 2025.10