# 题目大意给定 n n n 个数 a 1 , . . . , a n a_1,...,a_n a 1 , . . . , a n ,求最大的非负整数 x x x ,x x x 需要满足
∑ i = 1 n [ a i ≥ x ] ≥ x \sum\limits_{i=1}^{n} [a_i \ge x] \ge x i = 1 ∑ n [ a i ≥ x ] ≥ x # 数据范围1 ≤ n ≤ 100 1 \le n \le 100 1 ≤ n ≤ 1 0 0 0 ≤ a i ≤ 1 0 9 0 \le a_i \le 10^9 0 ≤ a i ≤ 1 0 9 # 题解x x x 最大不会超过 n n n ,因为是计数问题,最多个数也只能是 n n n ,所以倒着枚举 x x x 即可
c++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void solve () { cin >> n; vector<int > a (n + 1 ) ; for (int i = 1 ; i <= n; i++) cin >> a[i]; for (int i = n; i >= 0 ; i--) { int cnt = 0 ; for (int j = 1 ; j <= n; j++) if (a[j] >= i) cnt++; if (cnt >= i) { cout << i << '\n' ; return ; } } }
# 题目大意有一个周长为 L L L 的圆,圆周上有 N N N 个点,第 i + 1 i + 1 i + 1 个点,位于从第 i i i 个点顺时针走 d i d_i d i 距离的位置,请你统计满足以下条件三元组的个数
a , b , c a, b, c a , b , c 互不相同以 a , b , c a,b,c a , b , c 为顶点的三角形是正三角形 # 数据范围3 ≤ L , N ≤ 3 × 1 0 5 3 \le L, N \le 3 \times 10^5 3 ≤ L , N ≤ 3 × 1 0 5 0 ≤ d i ≤ L 0 \le d_i \le L 0 ≤ d i ≤ L 输入均为整数 # 题解显然,只有当 L L L 是 3 3 3 的倍数时,才有可能有等边三角形出现 我们不妨记录一个 c n t cnt c n t 数组,第一个位置随便选,选 0 0 0 位置即可,记录圆上各位置出现点的个数,为了避免重复计算,我们只需要遍历 i = 1 → i = l / 3 i = 1 \to i = l / 3 i = 1 → i = l / 3 即可计算所有可能的等边三角形,个数为三个位置点数相乘
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void solve () { cin >> n >> l; vector<int > d (n + 1 ) , cnt (l + 1 ) ; int last = 0 ; cnt[0 ]++; for (int i = 1 ; i <= n; i++) cin >> d[i], last = (last + d[i]) % l, cnt[last]++; cnt[last]--; if (l % 3 != 0 ) { cout << 0 << '\n' ; return ; } int res = 0 , k = l / 3 ; for (int i = 0 ; i <= k; i++) { int a1 = cnt[i], a2 = cnt[i + k], a3 = cnt[i + 2 * k]; res += a1 * a2 * a3; } cout << res << '\n' ; }
# 题目大意给定一个长度为 n n n 的小写字母字符串 S = s 1 s 2 . . . s n S=s_1s_2...s_n S = s 1 s 2 . . . s n ,现在对其执行一次 下方操作
选择一个长度至少为 1 1 1 的连续子串,将其向左循环移位 1 1 1 次 求出操作后,S S S 字典序最小的字符串
# 数据范围1 ≤ T , n ≤ 1 0 5 1 \le T,n \le 10^5 1 ≤ T , n ≤ 1 0 5 # 题解# 错解一开始的思路是,对于每一个 s i s_i s i ,其必须 ≥ s i + 1 \ge s_{i+1} ≥ s i + 1 才有移位的必要,然后其移位最优解是移动到 a → s i a \to s_{i} a → s i 的最后一个位置上 但是比如说 baca
,显然,最优移法是 abca
,但是按我的移法,最后答案会是 acab
由此可以发现,移动位置不是 a → s i a \to s_i a → s i 的每个的最后一个,而是在 s i s_i s i 之后,第一个 ≥ s i \ge s_i ≥ s i 的字符的前面一个位置,这是 W A WA W A 的原因 而为什么会 T L E TLE T L E 呢? 这是由于,s u b s t r substr s u b s t r 的时间复杂度是线性 O ( L ) O(L) O ( L ) 的,预估复杂度是 O ( 26 ⋅ N ⋅ L ) O(26·N·L) O ( 2 6 ⋅ N ⋅ L ) ,显然直接爆掉了
C++ 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 void solve () { cin >> n >> s; vector<vector<int >> pos (26 ); for (int i = 0 ; i < n; i++) pos[s[i] - 'a' ].push_back (i); string res = s; for (int i = 0 ; i < n - 1 ; i++) if (s[i] >= s[i + 1 ]) { for (int j = s[i] - 'a' ; j >= 0 ; j--) { int idx; if (pos[j].empty () || (idx = pos[j].back ()) < i) continue ; string t = s.substr (0 , i) + s.substr (i + 1 , idx - i) + s[i] + s.substr (idx + 1 , n - idx - 1 ); if (res > t) res = t; } } cout << res << '\n' ; }
# 正解如何解决 T L E TLE T L E 呢,题目要求是求字典序最小的,显然遇到能够移位的,先移位,肯定是最优的 所以,我们可以用 O ( n ) O(n) O ( n ) 枚举需要循环移位的字符,O ( n ) O(n) O ( n ) 找到移动的位置,O ( n ) O(n) O ( n ) 输出答案即可 平均时间复杂度是 O ( 3 n ) O(3n) O ( 3 n ) ,可以接受
这题也给我们一个启示,当题目要求字符串类似移位操作 后,字典序最小的结果,只需要考虑第一次移位即可,因为后面的移位结果肯定不会大于前面的
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void solve () { cin >> n >> s; for (int i = 0 ; i < n - 1 ; i++) if (s[i] > s[i + 1 ]) { int idx = i + 1 ; while (idx < n && s[idx] <= s[i]) idx++; idx--; string t = s.substr (0 , i) + s.substr (i + 1 , idx - i) + s[i] + s.substr (idx + 1 ); cout << t << '\n' ; return ; } cout << s << '\n' ; }
# 题目大意给定一个 N N N 个顶点的树,有 N − 1 N-1 N − 1 条边,每条边的权重为 W i W_i W i ,顶点 i i i 上有一个整数 x i x_i x i ,若 x i > 0 x_i > 0 x i > 0 ,代表此处有 x i x_i x i 个正电子,否则有 − x i -x_i − x i 个负电子,题目保证,全树电子和为 0 0 0 每次可以沿边移动 1 1 1 个正电子或负电子,消耗 W j W_j W j 能量,当正负电子位于同一顶点时,他们会相互抵消 求,使得所有电子全部消失所需要的最小总能量
# 数据范围2 ≤ N ≤ 1 0 5 2 \le N \le 10^5 2 ≤ N ≤ 1 0 5 ∣ x i ∣ ≤ 1 0 4 |x_i| \le 10^4 ∣ x i ∣ ≤ 1 0 4 0 ≤ w j ≤ 1 0 4 0 \le w_j \le 10^4 0 ≤ w j ≤ 1 0 4 # 题解这题的关键,是要发现,不管最终在哪个点抵消 ,答案都是一样的 也就是说,我们可以把 1 1 1 点当根节点,利用树形 D P DP D P ,就直接是,从叶节点往上不断移动电子的全部总能量 树形 D P DP D P 还是练的少了,不过这个题目的性质有点难发现
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void dfs (int u, int f) { s[u] = a[u]; for (auto [to, val] : g[u]) if (to != f) { dfs (to, u); s[u] += s[to], res += val * abs (s[to]); } } void solve () { cin >> n, a = vector <int >(n + 1 ), s = vector <int >(n + 1 ); for (int i = 1 ; i <= n; i++) cin >> a[i]; for (int i = 1 , u, v, w; i < n; i++) { cin >> u >> v >> w; g[u].push_back ({v, w}), g[v].push_back ({u, w}); } dfs (1 , 0 ); cout << res << '\n' ; }