我是靠谱客的博主 寒冷白昼,这篇文章主要介绍比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场)比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场),现在分享给大家,希望可以做个参考。

比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场)

训练网址

A. Intelligent Warehouse

  • 题意:求给定集合的偏序集的最长链,偏序关系为整除。
  • 动态规划。其实就是第一层枚举 a i a_i ai,第二层枚举倍数。但是这个题卡常好严重。
  • 忘掉了忘掉了,用素数优化呀。只需要找倍数为素数就可以了。
  • 有一个地方非常需要小心,就是如果用素数的话,那么就不可以加上 if(!cnt[i]) continue;. 因为枚举素数的话,比如x的4倍就枚举不到了,那么需要两次2倍才可以传过去。这个时候 f ( 2 ∗ i ) f(2*i) f(2i) 就有用了。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 10000010; int f[maxn], cnt[maxn]; int prime[maxn], sz; bool st[maxn]; int N; void sieve(int N) { for(int i = 2; i <= N; i++){ if(!st[i]) prime[sz++] = i; for(int j = 0; prime[j] <= N / i; j++){ st[i * prime[j]] = true; if(i % prime[j] == 0) break; } } } int main() { sieve(maxn - 1); scanf("%d", &N); int x; for(int i = 0; i < N; i++){ scanf("%d", &x); ++cnt[x]; ++f[x]; } int ans = 0; for(int i = 1; i <= 10000000; i++){ //if(!cnt[i]) continue; for(int j = 0; j < sz && (ll)prime[j] * i <= 10000000; j++){ int p = prime[j]; f[p * i] = max(f[i * p], f[i] + cnt[i * p]); } ans = max(ans, f[i]); } printf("%dn", ans); return 0; }

B. Intelligent Robot

  • 二维平面,给起点和终点,平面上有一些障碍,一个机器人从起点到终点需要走的最短距离是多少?可以接触障碍的端点也可以沿着障碍走,但是不能跨过障碍。
  • 这个题和2020南京站的那个滑雪的题目很相似,都是沿着端点走,一定是最小值。那么可以暴力判断点两两之间有没有障碍。然后建图后跑一个dijkstra即可。
  • dijkstra 边权即使是浮点数,即使边权有0,也不会超时。
  • 数组开小会引起超时!
  • 函数无返回值会引起非常莫名其妙的错误,比如加一个printf之后就会段错误(一部分是编译优化的引起的)
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include<bits/stdc++.h> #define x first #define y second using namespace std; const int maxn = 610, maxm = 400000; typedef pair<double, double> P; const double eps = 1e-8, INF = 1e18; int sign(double x) { if(fabs(x) < eps) return 0; if(x < 0) return -1; return 1; } P operator+(P a, P b) { return {a.x + b.x, a.y + b.y}; } P operator-(P a, P b) { return {a.x - b.x, a.y - b.y}; } double operator *(P a, P b) { return a.x * b.x + a.y * b.y; } double cross(P a, P b) { return a.x * b.y - a.y * b.x; } bool intersection(P a1, P a2, P b1, P b2) { double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1); double c3 = cross(b2 - b1, a2 - b1), c4 = cross(b2 - b1, a1 - b1); //下面不可以写成 <= return sign(c1) * sign(c2) < 0 && sign(c3) * sign(c4) < 0; } double dist(P a, P b) { //这个地方把 double 写成 return 了,函数无返回值导致访问了非法内存。 double dx = a.x - b.x, dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } int h[maxn], e[maxm], ne[maxm], idx; double w[maxm]; int maxx, maxy, N; P st[maxn], ed[maxn], start, goal, points[maxn]; int sz; void add(int a, int b, double c) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } bool vis[maxn]; double d[maxn]; double dijkstra(int s, int t, int n) { typedef pair<double, int> PII; for(int i = 1; i <= n; i++) d[i] = INF; priority_queue<PII, vector<PII>, greater<PII>> que; d[s] = 0; que.push({0, s}); while(que.size()){ auto p = que.top(); que.pop(); int u = p.y; if(vis[u]) continue; vis[u] = true; for(int i = h[u]; i != -1; i = ne[i]){ int v = e[i]; if(d[v] > d[u] + w[i]){ d[v] = d[u] + w[i]; que.push({d[v], v}); } } } return d[t]; } int main() { memset(h, -1, sizeof h); scanf("%d%d%d", &maxx, &maxy, &N); for(int i = 1; i <= N; i++){ scanf("%lf%lf%lf%lf", &st[i].x, &st[i].y, &ed[i].x, &ed[i].y); points[++sz] = st[i]; points[++sz] = ed[i]; } scanf("%lf%lf%lf%lf", &start.x, &start.y, &goal.x, &goal.y); points[++sz] = start; points[++sz] = goal; // for(int i = 1; i <= sz; i++){ // printf("*** %f %fn", points[i].x, points[i].y); // } for(int i = 1; i <= sz; i++){ for(int j = i + 1; j <= sz; j++){ bool flag = true; for(int k = 1; k <= N && flag; k++){ if(intersection(points[i], points[j], st[k], ed[k])) { flag = false; } } if(flag) { double dd = dist(points[i], points[j]); //printf("%d %d %fn", i, j, dd); add(i, j, dd), add(j, i, dd); } } } printf("%.4fn", dijkstra(sz - 1, sz, sz)); return 0; }

D. Router Mesh

E. Phone Network

  • 题意:一个长度n的数组,每个数在[ 1,m ]区间内,保证了每个数(在[ 1 , m ])至少出现过一次。对于每个 i ∈ [ 1 , m ],分别输出在原数列中,包含了 [ 1 , i ] 的所有种类数字的最短区间长度。
  • 别人的题解
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include<bits/stdc++.h> using namespace std; const int maxn = 200010, INF = 1e9; struct node { //mi[i] 表示从i开始,包含 1~x的最短区间,的右端点。 int l, r; int mi, len, lazy; }tr[maxn * 4]; int N, M; vector<int> w[maxn]; void pushup(int u) { tr[u].mi = min(tr[2 * u].mi, tr[2 * u + 1].mi); tr[u].len = min(tr[2 * u].len, tr[2 * u + 1].len); } void pushdown(int u) { int t = tr[u].lazy; if(t){ tr[2 * u].mi = tr[2 * u].lazy = t, tr[2 * u].len = t - tr[2 * u].r + 1; tr[2 * u + 1].mi = tr[2 * u + 1].lazy = t, tr[2 * u + 1].len = t - tr[2 * u + 1].r + 1; tr[u].lazy = 0; } } void build(int u, int l, int r) { tr[u].l = l, tr[u].r = r, tr[u].len = INF; tr[u].mi = tr[u].lazy = 0; if(l == r) return; int mid = (l + r) / 2; build(2 * u, l, mid), build(2 * u + 1, mid + 1, r); } void modify(int u, int l, int r, int c) { //if(tr[u].r < l || tr[u].l > r) return; if(l <= tr[u].l && tr[u].r <= r){ tr[u].mi = tr[u].lazy = c; tr[u].len = c - tr[u].r + 1; return; } pushdown(u); int mid = (tr[u].l + tr[u].r) / 2; if(l <= mid) modify(2 * u, l, r, c); if(r > mid) modify(2 * u + 1, l, r, c); pushup(u); } //找到 [l, r] 中小于 last 的最大位置。 //即 如果mi[k]<p(i),表示x+1未被包含在这个区间,所以右端点要移到p(i),即mi[k]=p(i) //因为last - mi[k] + 1 作为以 i 为左端点len,只能作为在 [pre, cnt] 的答案。 int find(int u, int l, int r, int last) { if(tr[u].mi >= last) return -1; if(tr[u].l == tr[u].r) return tr[u].l; pushdown(u); int mid = (tr[u].l + tr[u].r) / 2; int tmp = -1; tmp = find(2 * u + 1, l, r, last); if(tmp == -1 && l <= mid) tmp = find(2 * u, l, r, last); return tmp; } int main() { scanf("%d%d", &N, &M); for(int i = 1, x; i <= N; i++){ scanf("%d", &x); w[x].push_back(i); } build(1, 1, N); for(int i = 1; i <= M; i++){ int pre = 1, sz = w[i].size(); for(int j = 0; j < sz; j++){ int last = w[i][j]; int cnt = find(1, pre, last, last); if(cnt != -1) modify(1, pre, cnt, last); pre = last + 1; } if(w[i].back() + 1 <= N) modify(1, w[i].back() + 1, N, INF); printf("%d ", tr[1].len); } return 0; }

F. Design Problemset

  • 题意:
    在这里插入图片描述

  • 思路:我们发现,假如我们确定了制作的 problemset的 数量,就可以很简单的判断出此方案是否可行。我们二分答案。首先把左端点加起来,大于 R的话方案数显然是0,右端点加起来小于 L 方案数显然是0.

  • 二分的check具体来讲,我们首先看都取左区间的话,每类题目能否不超过最大的题目数量 a [ i ] a[i] a[i] 限制(超过返回false);然后我们看都取左端点能否大于等于 L(大于等于就返回 true);最后我们这样子取,假设要制作 x 个 problemset,我们取 min ⁡ { x ∗ r [ i ] , a [ i ] } min{x * r[i], a[i]} min{xr[i],a[i]},比较是否能够达到 L.

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include<bits/stdc++.h> using namespace std; const int maxn = 100010; typedef long long ll; ll L, R, N, l[maxn], r[maxn], a[maxn]; ll suml, sumr, sumr_l; bool check(ll x) { for(int i = 1; i <= N; i++) if((__int128)x * l[i] > a[i]) return false; if(suml >= L) return true; __int128 res = 0; for(int i = 1; i <= N; i++){ res += min((__int128)x * r[i], (__int128)a[i]); } return res >= (__int128)x * L; } int main() { scanf("%lld%lld%lld", &N, &L, &R); for(int i = 1; i <= N; i++){ scanf("%lld", &a[i]); } for(int i = 1; i <= N; i++){ scanf("%lld%lld", &l[i], &r[i]); suml += l[i], sumr += r[i], sumr_l += r[i] - l[i]; } if(suml > R || sumr < L) { printf("0n"); } else{ ll lb = 0, ub = 1e18; while(ub > lb){ ll mid = (lb + ub + 1) / 2; //printf("%lldn", mid); if(check(mid)) lb = mid; else ub = mid - 1; } printf("%lldn", lb); } return 0; }

G. Tree Projection

I. Walking Machine

  • 题意:一个地图上标着一些字母,表示在这个点要往哪个地方走。问有多少个点会走到地图外面。
  • 很简单的题,深搜就行。走到地图外面就记为0,走到一个地图里面的环上就记为1. 初始化为-1表示尚未搜到。
  • 为了方便开一个 vis 数组表示是否搜到,以及 f 数组表示这个点是什么类型的(走动地图外面还是走不到地图外面)。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<bits/stdc++.h> #define x first #define y second using namespace std; typedef pair<int, int>P; unordered_map<char, P> dir; const int maxn = 1010; char mz[maxn][maxn]; bool vis[maxn][maxn]; int f[maxn][maxn]; int N, M; int dfs(int x, int y) { vis[x][y] = true; auto p = dir[mz[x][y]]; int nx = x + p.x, ny = y + p.y; if (nx < 1 || nx > N || ny < 1 || ny > M) return f[x][y] = 0; if(vis[nx][ny] && f[nx][ny] == -1) return f[x][y] = 1; else if(vis[nx][ny]) return f[x][y] = f[nx][ny]; return f[x][y] = dfs(nx, ny); } int main() { memset(f, -1, sizeof f); dir['W'] = {-1, 0}, dir['S'] = {1, 0}, dir['A'] = {0, -1}, dir['D'] = {0, 1}; scanf("%d%d", &N, &M); for(int i = 1; i <= N; i++){ scanf("%s", mz[i] + 1); } for(int i = 1; i <= N; i++){ for(int j = 1; j <= M; j++){ if(!vis[i][j]) f[i][j] = dfs(i, j); } } int ans = 0; for(int i = 1; i <= N; i++){ for(int j = 1; j <= M; j++){ ans += (f[i][j] == 0); } } printf("%dn", ans); return 0; }

J. Matrix Subtraction

  • 题意:给一个 n × m ntimes m n×m 的矩阵。可以选中一个 a × b atimes b a×b 的子矩阵,把里面的所有数都减1(子矩阵是连续的一部分,不要记错),使得最后子矩阵的所有数都是0.
  • 一个矩阵全为零,等价于他的二维差分矩阵全为0. 因为要想使左上角元素(比如第一行第一列的元素)变成0,只能把它作为小矩形的左上角做区间减法。因此我们遍历二分差分矩阵,如果出现大于0的元素,就以它作为小矩阵的左上角,对小矩阵做区间减法。最后看看二维差分数组是否全部变成了0.
  • 有一个需要注意的地方是,遍离二维差分时,因为题目中要求只能减不能加,因此只能出现大于0的时候才减去,小于等于0的时候不可以。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<bits/stdc++.h> using namespace std; const int maxn = 1010; typedef long long ll; ll m[maxn][maxn]; int N, M, A, B; void insert(int x1, int y1, int x2, int y2, ll c) { m[x1][y1] += c; m[x2 + 1][y1] -= c; m[x1][y2 + 1] -= c; m[x2 + 1][y2 + 1] += c; } int main() { int T; scanf("%d", &T); while(T--){ scanf("%d%d%d%d", &N, &M, &A, &B); for(int i = 1; i <= N; i++){ fill(m[i], m[i] + M + 1, 0); } for(int i = 1; i <= N; i++){ for(int j = 1; j <= M; j++){ int x; scanf("%d", &x); insert(i, j, i, j, x); } } for(int i = 1; i + A - 1 <= N; i++){ for(int j = 1; j + B - 1 <= M; j++){ if(m[i][j] > 0) insert(i, j, i + A - 1, j + B - 1, -m[i][j]); } } bool flag = true; for(int i = 1; i <= N && flag; i++){ for(int j = 1; j <= M && flag; j++){ if(m[i][j]) flag = false; } } if(flag) printf("^_^n"); else printf("QAQn"); } return 0; }

最后

以上就是寒冷白昼最近收集整理的关于比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场)比赛题目训练系列10 (2020ICPC·小米 网络选拔赛第一场)的全部内容,更多相关比赛题目训练系列10内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部