题目:[[2024.10.24 题目]]
## 长方体
有 $n$ 个长方体,第 $i$ 个对角线的两个顶点为 $(x_0,y_0,z_0)$ 和 $(x_1,y_1,z_1)$。求至少被 $n-1$ 个长方体所覆盖的整点数量。
---
显然若答案求的是被正好 $n$ 个长方体覆盖的整点数量很好算,对于下标为 $0$ 的我们取最大值,对于下标为 $1$ 的我们取最小值,最后最小值前去最大值,三个维度乘起来即可。
想到这一步,转换到 $n-1$ 个长方体的求法就很简单了。我们可以枚举 $i$,假设 $i$ 不算入答案,那么对 $1\sim i-1$ 和 $i+1\sim n$ 的长方体我们就可以使用 $n$ 个长方形的求法,时间复杂度 $\mathcal O(n^2)$。不难想到答案只需要各个维度的最大值与最小值,因此可以做一下前缀最大值、前缀最小值、后缀最大值、后缀最小值,这样子时间复杂度就可以有效地降到 $\mathcal O(n)$。上面所述的方法有很大的问题,就是对于有些点,可能去掉多个不同的长方形仍然可以算在内,因此我们考虑反向容斥。
对于这种点,它们只可能是 $n$ 个长方形都共有的部分。因此我们提前将这一部分的体积算出来,假设有 $m$ 个长方形被去掉后 $n-1$ 个长方形仍然有共同的部分,那么答案就减去 $(m-1)\times V$,其中 $V$ 是这一部分的体积。问什么要减一呢?因为凡事都不能做得太绝对,毕竟「做人留一线,日后好相见。」
```cpp
#include <fstream>
using namespace std;
using LL = long long;
const LL kMaxN = 1e5 + 5;
ifstream cin("cube.in");
ofstream cout("cube.out");
struct Cube {
LL x0, y0, z0, x1, y1, z1;
} a[kMaxN];
LL premax[kMaxN][3], premin[kMaxN][3], sufmax[kMaxN][3], sufmin[kMaxN][3], n, ans;
int main() {
cin >> n;
for (LL i = 1; i <= n; i++) {
cin >> a[i].x0 >> a[i].y0 >> a[i].z0 >> a[i].x1 >> a[i].y1 >> a[i].z1;
}
premax[0][0] = premax[0][1] = premax[0][2] = -1e18;
premin[0][0] = premin[0][1] = premin[0][2] = 1e18;
for (LL i = 1; i <= n; i++) {
premax[i][0] = max(premax[i - 1][0], a[i].x0);
premax[i][1] = max(premax[i - 1][1], a[i].y0);
premax[i][2] = max(premax[i - 1][2], a[i].z0);
premin[i][0] = min(premin[i - 1][0], a[i].x1);
premin[i][1] = min(premin[i - 1][1], a[i].y1);
premin[i][2] = min(premin[i - 1][2], a[i].z1);
}
sufmax[n + 1][0] = sufmax[n + 1][1] = sufmax[n + 1][2] = -1e18;
sufmin[n + 1][0] = sufmin[n + 1][1] = sufmin[n + 1][2] = 1e18;
for (LL i = n; i >= 1; i--) {
sufmax[i][0] = max(sufmax[i + 1][0], a[i].x0);
sufmax[i][1] = max(sufmax[i + 1][1], a[i].y0);
sufmax[i][2] = max(sufmax[i + 1][2], a[i].z0);
sufmin[i][0] = min(sufmin[i + 1][0], a[i].x1);
sufmin[i][1] = min(sufmin[i + 1][1], a[i].y1);
sufmin[i][2] = min(sufmin[i + 1][2], a[i].z1);
}
for (LL i = 1; i <= n; i++) {
LL a1 = max(premax[i - 1][0], sufmax[i + 1][0]), a2 = min(premin[i - 1][0], sufmin[i + 1][0]);
LL b1 = max(premax[i - 1][1], sufmax[i + 1][1]), b2 = min(premin[i - 1][1], sufmin[i + 1][1]);
LL c1 = max(premax[i - 1][2], sufmax[i + 1][2]), c2 = min(premin[i - 1][2], sufmin[i + 1][2]);
if (a2 == 1e18 || b2 == 1e18 || c2 == 1e18) {
continue;
}
if (a1 <= a2 && b1 <= b2 && c1 <= c2) {
ans += (a2 - a1 + 1) * (b2 - b1 + 1) * (c2 - c1 + 1);
ans -= max(0LL, premin[n][0] - premax[n][0] + 1) *
max(0LL, premin[n][1] - premax[n][1] + 1) *
max(0LL, premin[n][2] - premax[n][2] + 1);
}
}
cout << ans + max(0LL, premin[n][0] - premax[n][0] + 1) *
max(0LL, premin[n][1] - premax[n][1] + 1) *
max(0LL, premin[n][2] - premax[n][2] + 1) << '\n';
return 0;
}
```
## 三角形
给定长度为 $n$ 的序列 $a$,第 $i$ 个数为 $a_i$。进行 $q$ 次询问,每次询问的格式为 $[l,r]$,求区间内是否能够选出三个不同的下标 $(i,j,k)$ 使得三条 $(a_i,a_j,a_k)$ 的线段可以组成三角形。
---
不会写,采用暴力。首先我们可以对区间 $[l,r]$ 内的所有元素都进行一个 $\mathcal O(n^3)$ 的枚举,然后判断是否满足条件。这种枚举是最暴力最纯洁得了,总时间复杂度 $\mathcal O(mn^3)$。
不难想到优化,假设 $a_i$ 和 $a_j$ 是最小的那两个,并且 $a_i\le a_j$,只枚举 $i$ 和 $j$,$k$ 肯定是选择满足 $a_k>a_j$ 的,且 $k$ 尽量小,这样子对于一个区间我们只需要花费 $\mathcal O(n^2)$,总时间复杂度 $\mathcal O(mn^2)$。
考虑继续优化。此时忽然想起小学数学老师告诉过我们的,对于一个数列,想要选择三个数的长度的线段组成三角形,只需要选择大小相邻的就行了。因此我们可以对 $[l,r]$ 内的元素排序,然后三个三个判断,这样子的总时间复杂度就降到了 $\mathcal O(mn)$。可以获得 $60$ 分。
**正解**:考虑最坏的情况,无法组成三角形。由于 $a_i+a_j>a_k$,因此最坏的情况是构造类斐波那契数列的 $a_k=a_i+a_j$ 的序列。由于斐波那契数列在 $10^{18}$ 的范围之内只有不到 $100$ 个数,因此大于 $100$ 的情况就可以直接输出有解,否则采用暴力判断。
## 区间
给定长度为 $n$ 的序列 $a$,第 $i$ 个数为 $a_i$,并给定 $m$ 和 $q$ 次操作,每次操作给出 $(o,l)$。若 $o=1$,则翻转区间 $[l,l+m-1]$ 内的元素;如果 $o=2$,则获取 $a_l$。对于所有的操作 $2$,求出答案的异或和。
---
采用暴力。对于操作一,我们直接使用两个指针 $i,j$ 通过交换来实现区间翻转;对于第二个操作直接输出 $a_l$ 即可。该算法的时间复杂度为 $\mathcal O(mq)$,在对应的数据范围中理论上只能获得 $30$ 分,但是由于数据过水,此做法实际上可以获得 $60$ 分。
## 图
有 $n$ 个结点,第 $i$ 个结点的点权为 $a_i$。若 $a_i\operatorname{and}a_j=0$($\operatorname{and}$ 是按位与),则 $i$ 与 $j$ 有一条边。初始时集合 ${} S=\emptyset$,进行 $n$ 次操作,每次加入一个结点到 $S$。加入结点 $v$ 有两种方式:
- 直接加入结点 $v$,代价为 $0$;
- 选择另一个结点 $u\in S$ 且 $u$ 与 $v$ 之间有一条边,代价为 $a_u$。
求能够获得的最大代价。
---
赛时一眼想到了最大生成树,但是由于大样例挂掉红温便没有继续调试,实际上我的代码改几行就可以通过。
我提交的代码是采用状态压缩动态规划的版本。令 $dp_i$ 表示为当前状态为二进制 $i$(第 $j$ 位为 $1$ 代表第 $j$ 个结点已经加入了集合,反之反之),那么我们可以枚举新加入哪一个结点,必须是 $i$ 里面没有的;再枚举和哪一个连边,找出最大值,然后转移即可。
时间复杂度为 $\mathcal O(2^nn^2)$,侥幸获得 ${} 40$ 分。
## 总结
- **排名**:$7$;
- **比赛分数**:$260/400$;
- **情况**:相比 [[2024.10.23 模拟赛]],没区别;
- **问题**:调不出题目就红温。