2021“MINIEYE杯”中国大学生算法设计超级联赛(6) 杭电多校6 题解

sdxjzsq

2021-08-06 11:16:50

Personal

## 目录 | 题号 | 题目 | 知识点 | 完成度 | | ---- | ------------------------------------------------------------ | -------- | ------ | | 1001 | HDU7025 [Yes, Prime Minister](https://acm.hdu.edu.cn/showproblem.php?pid=7025) | 线性筛,质数性质| √ | | 1005 | HDU7029 [Median](https://acm.hdu.edu.cn/showproblem.php?pid=7029) | 思维 | √ | | 1004 | HDU7028 [Decomposition](https://acm.hdu.edu.cn/showproblem.php?pid=7028) | - | - | | 1008 | HDU7032 [Command and Conquer: Red Alert 2](https://acm.hdu.edu.cn/showproblem.php?pid=7032) | 计算几何 | √ | | 1007 | HDU7031 [Power Station of Art](https://acm.hdu.edu.cn/showproblem.php?pid=7031) | - | - | | 1003 | HDU7027 [0 tree](https://acm.hdu.edu.cn/showproblem.php?pid=7027) | - | - | ## 1001 HDU7025 [Yes, Prime Minister](https://acm.hdu.edu.cn/showproblem.php?pid=7025) ### 题意 寻找一个区间 $[l,r]$ ,满足 $x$ 在区间中,且 $\sum_{i=l}^{r}$ 是一个质数。 ### 思路 经过分析发现,当 $l,r$ 均大于 $0$ 时,区间的长度不超过 $2$ ,可以证明所有长度超过 $2$ 的区间和一定不是质数,下面做出证明: - 当区间长度为奇数时, $$ \sum_{i=l}^{r} = {(l+r)\times(r-l+1)\over 2} = {(l+r)\over 2}\times(r-l+1) $$ 被 $(r-l+1)$ 也就是区间长度整除。 - 当区间长度为偶数时, $$ \sum_{i=l}^{r} = {(l+r)\times(r-l+1)\over 2} = {(r-l+1)\over 2}\times(l+r) $$ 被 $(r-l+1)/2$ 或者 $(l+r)$ 整除。 综上,在区间长度大于 $2$ ,且 $l,r$ 都大于 $0$ 的情况下区间和一定不是质数,于是我们只要判断一下 $[x,x],[(x-1),x],[x,(x+1)]$ 这三个区间即可。 但是当有负数的情况下呢?首先如果 $x$ 为负数,那么区间长度至少为 $2(-x)+2$,也就是先从 $x$ 到 $-x$ 使得和大于 $0$ ,然后再加数。 那么会不会有无解的情况呢?理论上说是没有的,因为即使 $x$ 为正,我们依然可以通过 $[-x,x]$ 获得 $0$ ,然后再去对 $x+1$ 检查 $[x+1,x+2],[x+1,x+1]$ 这两个区间,不行的话就再加上 $\pm(x+1)$ 继续往后检查,一定能找到一个解。 根据质数分布,其实很快就可以找到解,最多 $\log x$ 次。 ### code ``` cpp #include <cmath> #include <cstdio> using namespace std; int t; long long n; const int maxp = 10000001; int pr[maxp], prime[maxp], top = 0; int check(long long x) { if (x > 10000000) { for (int i = 1; 1ll * prime[i] * prime[i] <= x; ++i) if (x % prime[i] == 0) return 0; return 1; } return !pr[x]; } inline void getPrime(int maxprime) { pr[1] = 1; for (int i = 2; i <= maxprime; i++) { if (!pr[i]) prime[++top] = i; for (int j = 1; j <= top && i * prime[j] <= maxprime; j++) { pr[i * prime[j]] = 1; if (!(i % prime[j])) break; } } } int main() { getPrime(10000000); for (scanf("%d", &t); t--;) { scanf("%lld", &n); long long ans = 0; int flag = 1; if (n <= 0) flag = 0, ans = (-n * 2) + 1, n = -n + 1; if (check(n)) ans += 1; else if (check(2 * n + 1)) ans += 2; else if (flag && check(n * 2 - 1)) ans += 2; else { if (flag) ans += n * 2 + 1, ++n; else ans += 2, ++n; int orz = 1; while (orz) { if (check(n)) ans += 1, orz = 0; else if (check(n * 2 + 1)) ans += 2, orz = 0; else ans += 2, ++n; } } printf("%lld\n", ans); } return 0; } ``` ## 1005 HDU7029 [Median](https://acm.hdu.edu.cn/showproblem.php?pid=7029) ### 题意 求解是否能将 $1,2,\cdots ,n$ 分成 $m$ 个区间,使得每个区间的中位数为 $x$ 。 ### 思路 首先,肯定是把 $m$ 个数 ### code ```cpp ``` ## 1004 HDU7028 [Decomposition](https://acm.hdu.edu.cn/showproblem.php?pid=7028) 待补 ## 1008 [Command and Conquer: Red Alert 2](https://acm.hdu.edu.cn/showproblem.php?pid=7032) ### 题意 已知三维坐标系下有 $n$ 个点,代表 $n$ 个敌方士兵,我方狙击手的狙击范围是 $k$ (指能够狙击到坐标满足 $max⁡(|x_s−x_e|,|y_s−y_e|,|z_s−z_e|)\le k$ ),现在我方狙击手在 $(-10^{100},-10^{100},-10^{100})$ 的位置出发,每步只能向 $x,y,z$ 轴正方向中的一个方向移动一格,求能够狙击掉所有敌方士兵的最小的 $k$ 。 ### 思路 首先进行一个题面的转化:我们发现狙击范围其实就是以狙击手为中心点、边长为 $2k$ 的正方体,那么我们不妨将狙击手的位置移动到正方体的角上,变成狙击手能够狙击到坐标满足 $max⁡(x_e−x_s,y_e−y_s,z_e−z_s)\le 2k$ 的敌方士兵,转化之后问题便简单了一些。 现在我们考虑,狙击手每移动一步,都代表着在某个方向上的一层敌人都无法被狙击到(比如从 $x_0$ 移动到 $x_0+1$ ,满足 $x=x_0$ 的所有敌人都无法被狙击到了,所以必须提前消灭) 于是我们可以开三个优先队列( $set$ 也可以)分别对每个坐标排序,于是我们就会发现,如果想要移动一格,要提前让准备移动的坐标上的所有点都被消灭。 但是根据坐标处理,范围是 $1e9$ 的,于是我们转化为对于点处理,即每次通过扩大 $k$ 使得能够将某个点消灭,当发现某个坐标上的点全部被消灭时,将坐标移动到下一个有点的位置。 进一步分析发现,狙击手的位置是不需要每次记录的(也不好维护),只要分别设置为敌人对应坐标的最小值即可。 到现在,我们得到了最终的思路: 贪心不断扩大 $k$ 来删掉下一个待删掉的点,每次将狙击手位置置于三个坐标分别的最小值的位置,一直这样做下去直到删掉所有点,最终答案为 $\lceil {ans\over 2}\rceil$。 ### code ```cpp #include <algorithm> #include <cstdio> #include <map> #include <queue> using namespace std; const int maxn = 5e5 + 7; int t, n, tot, ans; priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> Q[3]; int p[maxn][4]; int main() { for (scanf("%d", &t); t--;) { for (int i = 0; i < 3; ++i) while (Q[i].size()) Q[i].pop(); int o[3]; ans = 0; scanf("%d", &n); for (int i = 1; i <= n; ++i) { for (int j = 0; j < 3; ++j) scanf("%d", &p[i][j]), Q[j].push({p[i][j], i}), o[j] = min(o[j], p[i][j]); p[i][3] = 1; } while (Q[0].size()) { int tmp[4] = {0, 0, 0, (int)3e9}; for (int i = 0; i < 3; ++i) { while (Q[i].size() && p[Q[i].top().second][3] == 0) Q[i].pop(); if (Q[i].size()) o[i] = Q[i].top().first; } if (!Q[0].size()) break; for (int i = 0; i < 3; ++i) { int no = Q[i].top().second; tmp[i] = max(p[no][(i + 1) % 3] - o[(i + 1) % 3], p[no][(i + 2) % 3] - o[(i + 2) % 3]); tmp[3] = min(tmp[3], tmp[i]); } ans = max(ans, tmp[3]); for (int i = 0; i < 3; ++i) if (tmp[3] == tmp[i]) p[Q[i].top().second][3] = 0, Q[i].pop(); } printf("%d\n", (ans + 1) >> 1); } } ```