比赛大图来源:pixiv_id=122244412
出题人:MarisaMagic
题意简述
给定一个格式为“小时:分钟”的时间 t t t ,判断在 14 : 00 14:00 14 : 00 前、14 : 00 ∼ 17 : 00 14:00\sim 17:00 14 : 00 ∼ 17 : 00 内 还是 17 : 00 17:00 17 : 00 后。
解题思路 (语法)
输入得到小时 h h h 和分钟 m m m ,由此可以得到总分钟数为 h × 60 + m h \times 60 + m h × 60 + m ,判断是否位于时间段内的分钟数即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) void marisa () { int h, m; char ch; cin >> h >> ch >> m; int t = h * 60 + m; if (t < 14 * 60 ) cout << "wei kai shi." << "\n" ; else if (t > 17 * 60 ) cout << "yi jie shu." << "\n" ; else cout << "bi sai zhong." << "\n" ; }int main () { ios; int T = 1 ; cin >> T; while (T -- ) marisa (); return 0 ; }
出题人:MarisaMagic
题意简述
给定一个整数 n n n 和一个字符串 s s s (s i ∈ { ′ 1 ′ , ′ 2 ′ } s_i \in \{ '1', '2' \} s i ∈ { ′ 1 ′ , ′ 2 ′ } )。初始有 n n n 个空位。从头开始遍历字符串 s s s :
每个放置了的元素从放置回合起,每回合产生 1 1 1 个单位价值,一个元素被撤除后不再产生价值。求能够获得的价值总和的最大值。
解题思路 (贪心,模拟)
每个元素每回合产生的价值都为 1 1 1 ,记占用 1 1 1 个空位的元素为元素 1 1 1 ,占用 相邻 2 2 2 个空位的元素为元素 2 2 2 。考虑贪心算法维护每一回合放置的元素 1 1 1 个数 c n t 1 cnt_1 c n t 1 和 2 2 2 的个数 c n t 2 cnt_2 c n t 2 :
设当前回合元素所需占用的空位个数为 v v v 。
基于上述贪心策略,可以使得每一回合产生的价值数是非降的,前后回合价值数差距不超过 1 1 1 。这样可以使得最终获得的价值总和最大。
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 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) void marisa () { int n; cin >> n; string s; cin >> s; long long ans = 0 ; vector<int > cnt (3 ) ; for (const auto & ch : s){ int v = ch - '0' ; if (n >= v){ cnt[v] ++ ; n -= v; }else if (v == 1 && cnt[2 ] > 0 ){ cnt[2 ] -- ; cnt[1 ] ++ ; n ++ ; } ans += cnt[1 ] + cnt[2 ]; } cout << ans << "\n" ; }int main () { ios; int T = 1 ; while (T -- ) marisa (); return 0 ; }
出题人:MarisaMagic
题意简述
计算斐波那契数列的前 n n n 项中有多少对二元组 ( i , j ) (i, j) ( i , j ) (i < j i < j i < j )满足乘积 f i × f j f_i \times f_j f i × f j 为偶数。
解题思路 (数学)
斐波那契数列有一个奇偶性规律,即数列每一项的奇偶性满足:奇、奇、偶、奇、奇、偶 …
可以发现,斐波那契数列的前 n n n 项有 x = ⌊ n 3 ⌋ x = \lfloor \frac{n}{3} \rfloor x = ⌊ 3 n ⌋ 个偶数。两项乘积为偶数包括 偶数乘偶数 和 偶数乘奇数 两种可能。因此,两种可能对答案的贡献如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) using ll = long long ;void marisa () { ll n; cin >> n; ll x = n / 3 ; cout << x * (x - 1 ) / 2 + x * (n - x) << "\n" ; }int main () { ios; int T = 1 ; while (T -- ) marisa (); return 0 ; }
出题人:MarisaMagic
题意简述
给定 n n n 个数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a 1 , a 2 , … , a n ,选择 m m m 个数使得这些数做 按位与运算 最大。
解题思路 (位运算、贪心)
输入的数二进制下最多 29 29 29 位,考虑从高位到低位遍历每个二进制位。对于当前二进制位 i i i 若有大于等于 m m m 个数满足第 i i i 位为 1 1 1 ,那么我们必须选择这一位并将 2 i 2^i 2 i 加到答案中,因为后面的位更低,即便全部加起来也不会比 2 i 2^i 2 i 更大。
如果当前第 k k k 位被选择,那么后续选择二进制位的候选数仅包含第 i i i 位为 1 1
1 的数。故我们在选择一位后可以对第 i i i 位不为 1 1 1 的数进行标记(一种方式是直接将数赋为 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 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) void marisa () { int n, m; cin >> n >> m; vector<int > a (n) ; for (auto & x : a) cin >> x; int ans = 0 ; for (int i = 29 ; ~i; i -- ){ int cnt = 0 ; for (int j = 0 ; j < n; j ++ ) cnt += (a[j] >> i) & 1 ; if (cnt >= m){ ans |= (1 << i); for (int j = 0 ; j < n; j ++ ) if (!((a[j] >> i) & 1 )) a[j] = 0 ; } } cout << ans << "\n" ; }int main () { ios; int T = 1 ; while (T -- ) marisa (); return 0 ; }
出题人:MarisaMagic
题意简述
给定 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a 1 , a 2 , … , a n (1 ≤ a i ≤ 2 n 1 \le a_i \le 2n 1 ≤ a i ≤ 2 n )。构造一个序列 b 1 , b 2 , … , b n b_1, b_2, \ldots, b_n b 1 , b 2 , … , b n (1 ≤ b i ≤ 2 n 1 \le b_i \le 2n 1 ≤ b i ≤ 2 n ),且 ∀ i ≠ j \forall i \neq j ∀ i = j ,b i ≠ b j b_i \neq b_j b i = b j ;同时 ∀ i \forall i ∀ i ,b i = i b_i = i b i = i 或 b i = a i b_i = a_i b i = a i 。
求序列 b b b 满足 b i = a i b_i = a_i b i = a i 的最大数量。
解题思路 (拓扑排序、dfs)
我们考虑对于每个 a i a_i a i ,构建一条 i → a i i \rightarrow a_i i → a i 的边。观察这些边构成的图可以发现,图中只有 单链 和 环 两种结构。
综上所述,答案分为两个部分:(1)在反向图上以每个大于 n n n 的点为起点进行 dfs 加上所有单链的长度(并标记这些点);(2)在剩余的正向图上进行拓扑排序,加上剩余遍历不到的环上的点的个数。
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 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) const int N = 2e5 + 10 ;int n, ind[N], vis[N]; vector<int > e[N], re[N];void dfs (int u, int dep, int & mx_dep) { if (u <= n){ vis[u] = true ; mx_dep = max (mx_dep, dep); } for (const auto & v : re[u]) dfs (v, dep + 1 , mx_dep); }void topo () { queue<int > q; for (int i = 1 ; i <= n; i ++ ) if (!vis[i] && !ind[i]) q.emplace (i); while (q.size ()){ int u = q.front (); q.pop (); vis[u] = true ; for (const auto & v : e[u]) if (!( -- ind[v])) q.emplace (v); } }void marisa () { cin >> n; for (int i = 1 , a; i <= n; i ++ ){ cin >> a; e[i].emplace_back (a); re[a].emplace_back (i); ind[a] ++ ; } int ans = 0 ; for (int i = n + 1 ; i <= 2 * n; i ++ ){ int mx_dep = 0 ; dfs (i, 0 , mx_dep); ans += mx_dep; } topo (); for (int i = 1 ; i <= n; i ++ ) ans += !vis[i]; cout << ans << "\n" ; }int main () { ios; int T = 1 ; while (T -- ) marisa (); return 0 ; }
出题人:South_Orange
题意简述
给点一棵包含 n n n 个节点的树,节点有黑色和白色。求相距最远的一对不同颜色节点的距离是多少。
解题思路 1 (树的遍历)
类似树的直径的解题思路,维护一个子树中最长和次长的黑色节点与白色节点链。
记录黑色最长链与白色最长链是否在同一条链上,如果不在同一条链上则答案与黑色最长链+白色最长链取 m a x max ma x ,否则如果次长链存在,答案与黑色最长链+白色次长链和黑色次长链+白色最长链取 m a x max ma x 。
最后答案与当前节点的不同颜色的最长链取m a x max ma x 。
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 #include <iostream> using namespace std;#include <vector> #include <algorithm> #include <set> #include <map> #include <queue> #include <cmath> #include <cstring> #include <iomanip> using ll = long long ;const int N = 1e5 + 10 ;int n;int col[N]; vector<int > v[N];int ans;struct Node { int bd, wd, cibd, ciwd; int bpos, wpos, cibpos, ciwpos; };Node dfs (int u, int fa) { Node res = { 0 ,0 ,0 ,0 }; for (auto it : v[u]) { if (it == fa) continue ; auto itres = dfs (it, u); if (itres.bd > res.bd) { res.cibd = res.bd; res.cibpos = res.bpos; res.bd = itres.bd; res.bpos = it; } else if (itres.bd > res.cibd) { res.cibd = itres.bd; res.cibpos = it; } if (itres.wd > res.wd) { res.ciwd = res.wd; res.ciwpos = res.wpos; res.wd = itres.wd; res.wpos = it; } else if (itres.wd > res.ciwd) { res.ciwd = itres.wd; res.ciwpos = it; } } if (res.bpos != 0 && res.wpos != 0 ) { if (res.bpos != res.wpos) { ans = max (ans, res.bd + res.wd); } else { if (res.ciwpos != 0 ) ans = max (ans, res.bd + res.ciwd); if (res.cibpos != 0 ) ans = max (ans, res.wd + res.cibd); } } if (col[u] == 0 ) { ans = max (ans, res.bd); res.wd++; if (res.bpos != 0 ) res.bd++; } else { ans = max (ans, res.wd); res.bd++; if (res.wpos != 0 ) res.wd++; } return res; }void solve () { cin >> n; for (int i = 1 ;i <= n;i++) cin >> col[i]; for (int i = 1 ;i < n;i++) { int a, b; cin >> a >> b; v[a].push_back (b); v[b].push_back (a); } dfs (1 , -1 ); cout << ans << '\n' ; }int main () { ios::sync_with_stdio (0 );cin.tie (0 );cout.tie (0 ); int T = 1 ; while (T--) { solve (); } return 0 ; }
解题思路 2 (树的直径)
类似于 两次dfs求树的直径 的做法,考虑任选一点(选择 1 1 1 号点)作为起点进行第一次 dfs,找到最远的白色点 f 0 f_0 f 0 和最远的黑色点 f 1 f_1 f 1 。并记录 s 0 = f 0 , s 1 = f 1 s_0 = f_0, s_1 = f_1 s 0 = f 0 , s 1 = f 1 ,作为后续 dfs 的起点。
第二次 dfs 以 s 0 s_0 s 0 为起点,找到对应最远的黑色点 f 1 f_1 f 1 ,由此求出 s 0 s_0 s 0 到对应最远黑点 f 1 f_1 f 1 的距离;
第三次 dfs 以 s 1 s_1 s 1 为起点,找到对应最远的白色点 f 0 f_0 f 0 ,由此求出 s 1 s_1 s 1 到对应最远白点 f 0 f_0 f 0 的距离。
答案为第二次 dfs 和 第三次 dfs 所求最远距离的最大值。不会有更大的距离。
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 #include <bits/stdc++.h> using namespace std;#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) void marisa () { int n; cin >> n; vector<int > c (n + 1 ) ; vector<vector<int >> e (n + 1 ); for (int i = 1 ; i <= n; i ++ ) cin >> c[i]; for (int i = 1 , u, v; i < n; i ++ ){ cin >> u >> v; e[u].emplace_back (v); e[v].emplace_back (u); } int ans = 0 , f0 = 0 , f1 = 0 ; vector<int > d (n + 1 ) ; function<void (int , int )> dfs = [&](int u, int fa){ for (const auto &v : e[u]){ if (v == fa) continue ; d[v] = d[u] + 1 ; if (!c[v] && d[v] > d[f0]) f0 = v; if (c[v] && d[v] > d[f1]) f1 = v; dfs (v, u); } }; dfs (1 , 0 ); int s0 = f0, s1 = f1; f0 = f1 = 0 ; d[s0] = 0 ; dfs (s0, 0 ); ans = max (ans, d[f1]); f0 = f1 = 0 ; d[s1] = 0 ; dfs (s1, 0 ); ans = max (ans, d[f0]); cout << ans << "\n" ; }int main () { ios; int T = 1 ; while (T -- ) marisa (); return 0 ; }
出题人:South_Orange
题意简述
给定平面直角坐标系上的 n n n 个点,求出距离最远的两个点的距离。
解题思路 (凸包)
平面最远点对问题可以等价于凸包直径问题。
首先我们需要求出点集的凸包。
然后我们考虑选定凸包的一条边所在的直线,比如 AB 。然后找到凸包的所有顶点中离它最远的点,在这个例子中是 D 。然后凸包直径就 可能 是 AD 或 BD 。
然后我们继续。逆时针选择下一条边 AE ,这时我们发现最远点变成了 C ,然后尝试用 AC,EC 更新答案。以此类推。这样我们就找到了凸包直径。
但是这样子时间复杂度是 O ( n 2 ) O(n^2) O ( n 2 ) 的,应该无法通过。
但是根据以前的经验,似乎最远点也是逆时针旋转的。换句话说,逆时针遍历的点到直线的距离单调。
这也可以用凸包的凸性来解释。我无法给出详细证明,但是大家不妨手动画几个图,就可以感性的理解了。
于是我们就可以用一个漂亮的双指针解决了。
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 #include <iostream> using namespace std;#include <vector> #include <algorithm> #include <set> #include <map> #include <queue> #include <cmath> #include <cstring> #include <iomanip> namespace Geo{ ... }using namespace Geo;using ll = long long ;const int N = 50010 ;int n;template <typename T>ll d2 (const Point<T>& A, const Point<T>& B) { return (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y); }void solve () { cin >> n; vector<Point<ll>> v; for (int i = 1 ;i <= n;i++) { ll a, b; cin >> a >> b; v.push_back (Point <ll>(a, b)); } auto poly = convex_hull (v); int m = poly.size (); int pos = 1 ; ll ans = 0 ; for (int i = 0 ;i < m;i++) { auto line = Line <ll>(poly[i % m], poly[(i + 1 ) % m]); while (point_line_dis (poly[pos % m], line) < point_line_dis (poly[(pos + 1 ) % m], line)) pos++; ans = max (ans, d2 (poly[i % m], poly[pos % m])); ans = max (ans, d2 (poly[(i + 1 ) % m], poly[pos % m])); } cout << ans << '\n' ; }int main () { ios::sync_with_stdio (0 );cin.tie (0 );cout.tie (0 ); int T = 1 ; while (T--) { solve (); } return 0 ; }
namespace Geo
可见:Geo.cpp