2025天津大学预推免机试题解

前言

汗流浃背的一集,本来以为能速通,结果差点被速通了

1.进位

题意

给定两个64位无符号整数,输出一个64位的01字符串,表示二进制下相加每一位的进位情况

思路

直接把读入的整数转为二进制字符串,%2 /2这样转化后模拟加法过程即可,但是要注意是64位无符号整数,不能直接用 long long读入,要用unsigned long long,不知道会挂多少人。

2.旋转

题意

三维空间里,给定过原点的一个直线 \((a, b, c)\) ,和一个坐标 \((x, y, z)\) 以及一个旋转角度 \(\theta\), 求该坐标以直线为轴,旋转 \(\theta\) 后的坐标位置。

思路

这题因为下面给出了具体的公式,其实是个模拟,写个普通的矩阵乘法,慢慢按照题意计算变量即可。

3.相交线段

题意

二维平面上有一个正方形 \(ABCD\), 这四个坐标是固定的。然后给 \(n\) 个正方形边上的点,满足 \(n \leq 10^4\), 对于这些点构成的 不平行于正方形边 的线段集合,求有多少对线段是 严格相交 的,也就是只有端点重合不算,一定要在线段内部有交点。

思路

按照四条边的顺序对正方形上的点进行排序,然后两层for循环暴力枚举点对构成的线段,假设当前枚举到的点是 \(i\)\(j\)且它不平行,那么能够满足与这个线段严格相交的两个点,必须一个位于 \(i, j\) 之间,一个在 \(i, j\) 外面,由于我们排序过,所以点对 \(k, l\) 必须满足 \(i < k < j\) 以及 \(1 <= l < i || j < l <= n\),不取等号保证了不会出现仅端点重合的情况,所以对于这个线段 \(i,j\) 的贡献是 \((j - i - 1) \times (i - 1 + n - j)\)

现在的问题在于这样枚举,一来是会把所有线段对计算了两遍,因为合法线段对的两个线段都被枚举到了。二来是没有考虑平行的线段是非法的,需要去除。所以需要进行一下简单的容斥。

先来考虑一下,前面的计算都算了哪些情况:在枚举 \(i, j\) 的时候已经保证了 \(i,j\) 不平行,但是计算贡献时候没有保证第二个线段不平行,所以多算了一个平行一个不平行的情况。所以考虑在枚举 \(i,j\) 的时候,如果发现 \(i,j\) 平行,反而要减去它对应的贡献。那么现在一个平行一个不平行的情况已经没有了,但是前面减去会导致两个都平行的情况被额外减去了一次,所以考虑再加上两个都平行的线段对数(这个数等价于平行 \(x\) 轴数量 \(\times\) 平行 \(y\) 轴的线段数)。那么现在,就只有合法的线段对被计算了2次,最终输出答案再除以2即可。这样做是 \(O(n^2)\) 的, \(n \leq 10^4\) 有可能卡的过去,因为枚举时候只要枚举一半,不知道有没有更优的做法。

4.平方树

题意

给定一棵 \(n\) 个结点的树,每个结点有权值 \(val[i]\), 有 \(q\) 次操作,每次操作要么选择一个节点,让该节点的子树内所有节点的权值开根号向下取整,要么查询某个节点此时的权值。 \(n \leq 10^5, q \leq 10^5\)

思路

这题首先要发现一个性质,就是开根号的有意义次数是有限的,一个数开到1之后再开根号不改变值,所以每个节点的最多有效次数大概是 \(log (val[i])\) 次。然后有一个经典trick是一个树的子树在DFN序上是连续的一个区间(参考23年新生赛世界树),明显子树问题要先处理出这个dfn序列,之后那么就有两种做法:

1.并查集

直接用并查集维护一下已经被开到1的那些区间,操作1就暴力遍历子树对应的区间并对每个数进行开根号操作,用并查集跳过已经都是1的区间,这样均摊下来是 \(O(能过)\),(具体大概是\(O(nlog^2)\)左右)

2.树状数组

我的做法是用树状数组差分维护一下dfn序列里每个位置被开了多少次根号,再开两个数组维护每个位置当前的数值 \(nowval[i]\) 和从开头到现在被开了多少次根号 \(nowcnt[i]\)
那么操作1就是树状数组两次单点修改,操作2的话,先树状数组查询一下当前位置应该要被开的根号次数 \(cnt\),然后不断开根号,直到 \(nowcnt[i] == cnt\) 或者 \(nowval[i] <= 1\)为止,然后输出 \(nowval[i]\)。这个复杂度大概是 \(O(nlogn)\),因为开根号的log和树状数组的log是分开的。
其实这题还有很多方法能维护,这个开根号的性质很像势能线段树,所以其实也可以无脑用线段树维护dfn序列,维护区间最大值,操作1区间修改就是暴力递归下去,直到区间的max小等于1就返回,均摊下来也差不多是 \(nlogn\) 的,操作2就是单点查询。所以用这种方法其实可以支持操作2变成区间查询,并查集就维护不了这个区间和了。

5.跳格子

题意

有两个人 \(TJUHuangTao\)\(totalhuang\) 分别在两个 \(m\times n\) 的二维地图上 \(A[m][n]\)\(B[m][n]\) 跳格子。(这道题 \(n\)\(m\) 跟正常的感觉是反着的很难受),地图上每个位置有一个权值 \(A[i][j]\)\(B[i][j]\),两个人只能向右或者向上跳,并且两个人必须同步地跳,且保证每次跳到的格子的权值差小等于 \(x\),也就是假设当前 \(TJUHuangTao\) 在坐标 \((i, j)\)\(totalhuang\) 在坐标 \((k, l)\) 那么必须满足 \( |A[i][j] - B[k][l]| \leq x\),问两个人从 \((0, 0)\) 走到 \((n - 1, m - 1)\) 总的有多少种不同的路径,对 \(10^9 + 7\) 取余。
数据范围 \(n \times m \leq 10^3\) 保证出发点是合法的

思路

这个跳格子只能往上往右,是动态规划里面的经典问题,设 \(dp[i][j]\)表示一个人到了位置 \((i, j)\)等路径方案数,那么转移方程是 \(dp[i][j] = dp[i - 1][j] + dp[i][j - 1]\),这里变成两个人相当于要同时记录四个维度,但是 \(n\times m \leq 10^3\) 如果记录四个维度转移时候很容易爆(现在回想一下好像可能也能刚好卡过去)。
但是因为题目规定两个人必须同步跳,那么一定会符合条件 \(i + j = k + l\),所以只需要枚举前三个变量,第四个变量是固定的。
所以就先定义一个函数

bool hefa(int x1, int y1, int x2, int y2)
	if (x1 == 0 || y1 == 0 || x2 == 0 || y2 == 0) return false;
	return abs(A[x1][y1] - B[x2][y2]) <= x;

表示当前 \(TJUHuangTao\) 在位置 \((x1, y1)\), \(totalhuang\) 在位置 \((x2, y2)\) 的时候是否合法。
那么定义 \(dp[i][j][k]\) 表示当前 \(TJUHuangTao\) 在位置 \((i, j)\)\(totalhuang\) 在位置 \((k, i + j - k)\) 时的方案数,明显有转移方程:

\[dp[i][j][k] = (dp[i - 1][j][k] + dp[i - 1][j][k - 1] + dp[i][j - 1][k] + dp[i][j - 1][k - 1]) \mod 10^9+7 \]

这里加的几个项需要满足位置合法,用几个if拆开加即可。
复杂度 \(O(m*n*m)\)

posted @ 2025-09-09 12:12  TJUHuangTao  阅读(34)  评论(0)    收藏  举报