比赛链接
A. 蚂蚁上树
题目大意
给定一棵 n n n 个结点的树,根结点为 1 1 1。每个 叶结点 都有一只蚂蚁,每过 1 1 1 秒钟,你可以选一些蚂蚁往其 父结点 走一步,但是要求任意两只蚂蚁都不能在同一个 非根结点 上。
问至少要多少时间,使得所有蚂蚁都走到 根节点。
数据范围
- 2 ≤ n ≤ 5 ⋅ 1 0 5 . 2 \leq n \leq 5 \cdot 10^5. 2≤n≤5⋅105.
Solution
显然,根结点 1 1 1 的每个儿子都是独立的,所以下面考虑其中一个儿子 x x x。
直觉上考虑,对于越深的结点,应该越迟到 x x x,所以我们可以将 x x x 下所有的叶子按照深度从小到大排序,初始化 d = 0 d = 0 d=0,接着顺序遍历这些结点,然后不断令 d = max ( d + 1 , d e p i ) d = \max(d + 1, dep_i) d=max(d+1,depi),其中 d e p i dep_i depi 表示结点 i i i 的深度。
这表示,如果深度相同,需要等我往上走一步,再轮到你;否则你至少比我落后一步。
答案就是每个儿子 d d d 的最大值。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)
C++ Code
#include <bits/stdc++.h>int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n;std::cin >> n;std::vector<std::vector<int>> adj(n);for (int i = 1; i < n; i++) {int u, v;std::cin >> u >> v;u--, v--;adj[u].push_back(v);adj[v].push_back(u);}std::vector<int> dep(n);std::vector<int> par(n, -1);std::vector<std::vector<int>> leaf(n);auto dfs = [&](auto &&self, int x, int bel) -> void {if (x != bel and adj[x].empty()) {leaf[bel].push_back(x);}for (int y: adj[x]) {adj[y].erase(std::ranges::find(adj[y], par[y] = x));dep[y] = dep[x] + 1;self(self, y, bel != 0 ? bel: y);}};dfs(dfs, 0, 0);if (std::ranges::max(dep) == 1) {std::cout << 1 << "\n";return 0;}int ans = 1;for (auto &l: leaf) {std::ranges::sort(l, [&](int x, int y) {return dep[x] < dep[y];});int res = 0;for (int x: l) {res = std::max(res + 1, dep[x]);}ans = std::max(ans, res);}std::cout << ans << "\n";return 0;
}
B. 奶龙大战贝利亚
题目大意
奶龙和贝利亚在玩一个游戏,给出一个 n × m n \times m n×m 的棋盘,棋盘上摆满了黑白棋。
每一轮玩家可以按顺序执行如下操作:
- 选择其中一行,该行 至少 存在一个黑面朝上的棋子;
- 从该行选择至少一个棋子,且选择的棋子中最左边的那个棋子必须为黑面朝上;
- 将选择的棋子依次翻转(黑变白,白变黑)。
两人轮流依次进行操作,奶龙先手。
如果轮到某人时无法操作,则此人输掉了游戏。
给定起始盘面, W W W 表示该格棋子是白面朝上, B B B 表示该格棋子是黑面朝上。
奶龙想知道在两人都足够聪明的情况下,它是否有必胜方案。
数据范围
- 1 ≤ n × m ≤ 1 0 6 . 1 \leq n \times m \leq 10^6. 1≤n×m≤106.
Solution
赛时是猜了三发猜出来的,证明的话可以直接看 官解。
把 B B B 看作 1 1 1, W W W 看作 0 0 0,然后我们就有了 n n n 个长度均为 m m m 的二进制数。
那么结论就是:这 n n n 行的二进制数异或和不为 0 0 0,先手必胜。
时间复杂度 O ( n m ) O(\rm{nm}) O(nm)
C++ Code
#include <bits/stdc++.h>int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n, m;std::cin >> n >> m;auto s = std::string(m, '0');for (int i = 0; i < n; i++) {std::string g;std::cin >> g;for (int j = 0; j < m; j++) {if (g[j] == 'B') {s[j] ^= 1;}}}std::cout << (std::ranges::count(s, '1') > 0 ? "Yes\n": "No\n");return 0;
}
C. 我是奶龙
题目大意
给定 T T T 组数据,每组给定三个非负整数 n , m , k n, m, k n,m,k。
对于每组 n , m , k n, m, k n,m,k,它表示:
- 有一群奶龙变成了三种颜色,其中 n n n 条红奶龙, m m m 条蓝奶龙, k k k 条黄奶龙。当 两条不同颜色 的奶龙相遇时,它们会为了证明自己是真奶龙都会 变为第三种颜色,每个时刻只有一对奶龙相遇,问一段时间后,所有奶龙颜色是否可能相同?
数据范围
- 1 ≤ T ≤ 1 0 5 , 1 \leq T \leq 10^5, 1≤T≤105,
- 0 ≤ n , m , k ≤ 1 0 18 . 0 \leq n, m, k \leq 10^{18}. 0≤n,m,k≤1018.
Solution
算是个小数学题,我们一步步来推导。令 s = n + m + k s = n + m + k s=n+m+k。
假设红奶龙( R R R)和蓝奶龙( B B B)相遇,他们都会变成黄奶龙( Y Y Y),那么就有
n : = n − 1 , m : = m − 1 , k : = k + 2. \begin{align*} n &:= n - 1, \\ m &:= m - 1, \\ k &:= k + 2. \end{align*} nmk:=n−1,:=m−1,:=k+2.
观察发现,任意两者之间的差值关于 3 3 3 的余数都没有变化。
从结果出发,即 ( s , 0 , 0 ) , ( 0 , s , 0 ) , ( 0 , 0 , s ) (s, 0, 0), (0, s, 0), (0, 0, s) (s,0,0),(0,s,0),(0,0,s) 其中之一,我们发现最终的 n ′ , m ′ , k ′ n', m', k' n′,m′,k′ 一定满足:
- 其中两个数对 3 3 3 的余数是相同的。
时间复杂度 O ( T ) O(T) O(T)
C++ Code
#include <bits/stdc++.h>using i64 = long long;void solve() {i64 n, m, k;std::cin >> n >> m >> k;std::set<int> s {n % 3, m % 3, k % 3};if (s.size() < 3) {std::cout << "Yes\n";} else {std::cout << "No\n";}
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int T = 1;std::cin >> T;while (T--) {solve();}return 0;
}
D. 多米诺骨牌
题目大意
还是在牛客看题面吧()
Solution
由于我是 0-indexed
,所以下面的叙述全都是在原题的基础上下标 − 1 -1 −1。
原题中 a i + 0.5 a_i + 0.5 ai+0.5 的位置,设其高度为 h h h(这是整数),能覆盖的区间是 [ a i + 0.5 − h , a i + 0.5 ] [a_i + 0.5 - h, a_i + 0.5] [ai+0.5−h,ai+0.5],整点覆盖区间就是 ( a i + 0.5 − h , a i + 0.5 ) (a_i + 0.5 - h, a_i + 0.5) (ai+0.5−h,ai+0.5),由于浮点有误差,所以我们可以将其变为整点,也就是 a i : = a i + 0.5 a_i := a_i + 0.5 ai:=ai+0.5,这样覆盖区间就变成 [ a i − h , a i ) [a_i - h, a_i) [ai−h,ai)(此时 a i a_i ai 是整点)。
根据题意,覆盖到的区间可以传递,因此我们只要保证 [ 0 , n ) [0, n) [0,n) 都能被覆盖到。因此对于 [ a i − h , a i ) [a_i - h, a_i) [ai−h,ai) 这样的覆盖区间,我们可以用差分统计,最后做一下前缀和得到 s s s 数组。
设现在未覆盖的点(即 s i = 0 s_i = 0 si=0 的位置)有 s u m \rm{sum} sum 个,对于当前的操作,我们把 a x a_x ax 改为 y y y,那么 a x a_x ax 的覆盖区间 [ a x − h , a x ) [a_x - h, a_x) [ax−h,ax) 会变为 [ a x − h + 1 , a x ) [a_x - h + 1, a_x) [ax−h+1,ax),因为高度少了 1 1 1,而对于 y y y, [ y − h , y ) [y - h, y) [y−h,y) 会扩展为 [ y − h − 1 , y ) [y - h - 1, y) [y−h−1,y)。
这样就可以在 a x − h a_x - h ax−h 上和 y − h − 1 y - h - 1 y−h−1 上进行单点修改。
时间复杂度 O ( n + q ) O(n + q) O(n+q)
C++ Code
#include <bits/stdc++.h>int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n, m;std::cin >> n >> m;std::vector<int> a(n);std::vector<int> h(n + 2);for (int i = 0; i < n; i++) {std::cin >> a[i];h[a[i]]++;}std::vector<int> s(n + 1);for (int i = 1; i <= n; i++) {s[std::max(i - h[i], 0)]++;s[i]--;}int sum = s[0] == 0;for (int i = 1; i < n; i++) {s[i] += s[i - 1];sum += s[i] == 0;}while (m--) {int x, y;std::cin >> x >> y;x--;int &p = a[x];if (p >= h[p] and --s[p - h[p]] == 0) {sum++;}h[p]--;h[y]++;if (y >= h[y] and s[y - h[y]]++ == 0) {sum--;}p = y;std::cout << sum << "\n";}return 0;
}
E. 找出猫猫虫
题目大意
给定 N N N 对数 ( L i , R i ) (L_i, R_i) (Li,Ri),要求你构造一个数组 X X X,满足:
- ∣ X ∣ = N |X| = N ∣X∣=N;
- ∀ i ∈ [ 1 , N ] \forall i \in [1, N] ∀i∈[1,N],均有 X i ∈ [ L i , R i ] X_i \in [L_i, R_i] Xi∈[Li,Ri];
- ∑ i = 1 N X i = 0 \sum\limits_{i = 1}^{N}X_i = 0 i=1∑NXi=0。
数据范围
- 1 ≤ N ≤ 2 ⋅ 1 0 5 , 1 \leq N \leq 2 \cdot 10^5, 1≤N≤2⋅105,
- − 1 0 9 ≤ L i , R i ≤ 1 0 9 . -10^9 \leq L_i, R_i \leq 10^9. −109≤Li,Ri≤109.
Solution
很明显我们需要贪心。
首先令 s L = ∑ i = 1 N L i , s R = ∑ i = 1 N R i sL = \sum\limits_{i = 1}^{N}L_i, \ sR = \sum\limits_{i = 1}^{N}R_i sL=i=1∑NLi, sR=i=1∑NRi,若 s L > 0 sL > 0 sL>0 或 s R < 0 sR < 0 sR<0,则一定无法满足 ∑ i = 1 N X i = 0 \sum\limits_{i = 1}^{N}X_i = 0 i=1∑NXi=0。
否则我们从 s L sL sL 开始,尝试往里填充 − s L -sL −sL。令 a d d = − s L add = -sL add=−sL,每次需要填充的数为 min ( a d d , R i − L i ) \min(add, R_i - L_i) min(add,Ri−Li),最后判断 a d d add add 是否减到 0 0 0 即可。
时间复杂度 O ( N ) O(N) O(N)
C++ Code
#include <bits/stdc++.h>using i64 = long long;template<class T, class U>
std::istream &operator>>(std::istream &is, std::pair<T, U> &p) {is >> p.first >> p.second;return is;
}
template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;}return is;
}
template<class T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {os << v[0];for (size_t i = 1; i < v.size(); i++) {os << " " << v[i];}return os;
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int N;std::cin >> N;std::vector<std::pair<int, int>> cat(N);std::cin >> cat;i64 sL = 0;i64 sR = 0;for (const auto &[L, R]: cat) {sL += L;sR += R;}if (sL > 0 or sR < 0) {std::cout << "No\n";return 0;}i64 add = -sL;std::vector<int> X;X.reserve(N);for (const auto &[L, R]: cat) {X.push_back(L);int x = std::min<i64>(R - L, add);X.back() += x;add -= x;}if (add == 0) {std::cout << "Yes\n";std::cout << X << "\n";} else {std::cout << "No\n";}return 0;
}
F. 点灯
题目大意
这个讲起来有点麻烦,可以直接去题目链接里看题意,大致就是放灯泡,灯泡之间不能互相照亮,求最多能放的灯泡数。
数据范围
- 2 ≤ n , m ≤ 200 , 2 \leq n, m \leq 200, 2≤n,m≤200,
- 0 ≤ a i , j < 2. 0 \leq a_{i, j} < 2. 0≤ai,j<2.
Solution
说实话我记得在某届篮球杯看到过类似的题,当时群友说的就是二分图最大匹配,所以我就试了一下,然后也算歪打正着对了。
看官解的描述是这样转化的:
- 称横向每一段黑色格子的右边线为横墙,竖向每一段黑色格子左边线为竖墙。每一个灯泡都对应唯一一段横墙和竖墙。为了使得两个灯泡不互相照亮,每一个横墙或竖墙都得唯一对应一个灯泡。将每一段白色格子所对应横墙和竖墙连一条边,问题就转化成了二分图最大匹配问题。
时间复杂度 O ( n 3 ) O(n^3) O(n3)
- 我这个板子好久了,但是复杂度应该蛮低的,可能不到 O ( n 3 ) O(n^3) O(n3)。
C++ Code
#include <bits/stdc++.h>using i64 = long long;template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;}return is;
}struct augment_path {std::vector<std::vector<int>> g;std::vector<int> pa; // 匹配std::vector<int> pb;std::vector<int> vis; // 访问int n, m; // 两个点集中的顶点数量int dfn; // 时间戳记int res; // 匹配数augment_path(int _n, int _m): n{_n}, m{_m} {assert(0 <= n and 0 <= m);pa = std::vector<int>(n, -1);pb = std::vector<int>(m, -1);vis = std::vector<int>(n);g.resize(n);res = 0;dfn = 0;}void add(int from, int to) {assert(0 <= from and from < n and 0 <= to and to < m);g[from].push_back(to);}bool dfs(int v) {vis[v] = dfn;for (int u: g[v]) {if (pb[u] == -1) {pb[u] = v;pa[v] = u;return true;}}for (int u: g[v]) {if (vis[pb[u]] != dfn and dfs(pb[u])) {pa[v] = u;pb[u] = v;return true;}}return false;}int match() {while (true) {dfn += 1;int cnt = 0;for (int i = 0; i < n; i += 1) {if (pa[i] == -1 and dfs(i)) {cnt += 1;}}if (cnt == 0) {break;}res += cnt;}return res;}
};int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n, m;std::cin >> n >> m;std::vector g(n, std::vector(m, 0));for (auto &v: g) {std::cin >> v;}int N = 0;std::vector row(n, std::vector(m, -1));for (int i = 0; i < n; i++) {int k = -1;for (int j = 0; j < m; j++) {if (g[i][j] == 1) {k = -1;continue;}if (k < 0) {k = N++;}row[i][j] = k;}}int M = 0;std::vector col(n, std::vector(m, -1));for (int j = 0; j < m; j++) {int k = -1;for (int i = 0; i < n; i++) {if (g[i][j] == 1) {k = -1;continue;}if (k < 0) {k = M++;}col[i][j] = k;}}augment_path ag(N, M);for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (g[i][j] == 0) {ag.add(row[i][j], col[i][j]);}}}std::cout << ag.match() << "\n";return 0;
}
G. 俄罗斯方块
题目大意
给定七个俄罗斯方块的数量,分别为 I , O , T , J , L , S , Z I, O, T, J, L, S, Z I,O,T,J,L,S,Z,保证 T = S = Z = 0 T = S = Z = 0 T=S=Z=0。下图为示例。
现在要拼成一个 2 × 2 K 2 \times 2K 2×2K 的矩阵,要求矩阵的每个格子都要被其中一个方块占据,且方块不能超出 2 × 2 K 2 \times 2K 2×2K 这个矩阵的边缘。同时,方块不能翻转,只能旋转。
求 K K K 的最大值。
数据范围
- 0 ≤ I , O , T , J , L , S , Z ≤ 1 0 9 , 0 \leq I, O, T, J, L, S, Z \leq 10^9, 0≤I,O,T,J,L,S,Z≤109,
- T = S = Z = 0. T = S = Z = 0. T=S=Z=0.
Solution
显然 O O O 这个四四方方的直接往上放就好。
对于 I , J , L I, J, L I,J,L,第一想法都是两个 I I I 变成 2 × 4 2 \times 4 2×4,两个 J J J( L L L) 变成 2 × 4 2 \times 4 2×4。
但是有一种特殊情况,就是 I I I 作为底,左上角一个 L L L,右上角一个 J J J,这样能凑出 2 × 6 2 \times 6 2×6。而这个 2 × 6 2 \times 6 2×6 最多只出现一次,因为假设出现 2 2 2 次,就相当于 2 × 12 2 \times 12 2×12,根据上面所说,可以变成 3 3 3 个 2 × 4 2 \times 4 2×4,分别是 2 I 2I 2I 组合, 2 J 2J 2J 组合, 2 L 2L 2L 组合,这是等价的。
时间复杂度 O ( 1 ) O(1) O(1)
C++ Code
#include <bits/stdc++.h>using i64 = long long;int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);i64 I, O, T, J, L, S, Z;std::cin >> I >> O >> T >> J >> L >> S >> Z;i64 min = std::min({I, J, L});int i = I % 2;int j = J % 2;int l = L % 2;int s = i + j + l;i64 ans = O;if (min == 0) {ans += (I - i) + (J - j) + (L - l);} else {ans += (I + J + L) - std::min(s, 3 - s);}std::cout << ans << "\n";return 0;
}
H. Super big cup
题目大意
给定一个长度为 n n n 的数组 a a a,对其做 m m m 次操作,每次操作如下:
- 给定区间 [ l , r ] ⊂ [ 1 , n ] [l, r] \subset [1, n] [l,r]⊂[1,n] 和 x x x,然后在此区间内随机等概率地选择一个 p p p,令 a p = x a_p = x ap=x。
求 m m m 次操作后每个 a i a_i ai 的期望。答案对 998244353 998244353 998244353 取模。
数据范围
- 1 ≤ n , m ≤ 2 ⋅ 1 0 5 , 1 \leq n, m \leq 2 \cdot 10^5, 1≤n,m≤2⋅105,
- 1 ≤ a i ≤ 1 0 9 , 1 \leq a_i \leq 10^9, 1≤ai≤109,
- 1 ≤ l ≤ r ≤ n , 1 \leq l \leq r \leq n, 1≤l≤r≤n,
- 0 ≤ x ≤ 1 0 9 . 0 \leq x \leq 10^9. 0≤x≤109.
Solution
对于一个 [ l , r ] [l, r] [l,r],考虑 i ∈ [ l , r ] i \in [l, r] i∈[l,r], a i a_i ai 有 p = 1 r − l + 1 p = \frac{1}{r - l + 1} p=r−l+11 的概率被选中被改为 x x x,有 1 − p = r − l r − l + 1 1 - p = \frac{r - l}{r - l + 1} 1−p=r−l+1r−l 的概率不变,因此有 a i ′ = ( 1 − p ) a i + p ⋅ x . a_i' = (1 - p)a_i + p\cdot x. ai′=(1−p)ai+p⋅x. 于是我们发现了熟悉的形式:对旧值乘一个系数,加一个偏移量,这就是线段树的经典题目了。
简单来说,维护乘积积 m u l \rm{mul} mul,以及偏移量 a d d \rm{add} add,每次修改只要
m u l : = ( 1 − p ) ⋅ m u l , a d d : = ( 1 − p ) ⋅ a d d + p ⋅ x . \begin{align*} mul &:= (1 - p)\cdot mul, \\ add &:= (1 - p) \cdot add + p \cdot x. \end{align*} muladd:=(1−p)⋅mul,:=(1−p)⋅add+p⋅x.
时间复杂度 O ( ( n + m ) log n + m log V ) O(\rm{(n + m)}\log n + m\log V) O((n+m)logn+mlogV)
- 其中 log n \log n logn 是线段树操作, log V \log V logV 是求逆元操作。
C++ Code
#include <bits/stdc++.h>using i64 = long long;template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;}return is;
}template<class T>
constexpr T power(T a, i64 b) {T res = 1;for (; b; b /= 2, a *= a) {if (b % 2) {res *= a;}}return res;
}
template<int P>
struct MInt {int x;constexpr MInt() : x{} {}constexpr MInt(i64 x) : x{norm(x % getMod())} {}static int Mod;constexpr static int getMod() {if (P > 0) {return P;} else {return Mod;}}constexpr static void setMod(int Mod_) {Mod = Mod_;}constexpr int norm(int x) const {if (x < 0) {x += getMod();}if (x >= getMod()) {x -= getMod();}return x;}constexpr int val() const {return x;}explicit constexpr operator int() const {return x;}constexpr MInt operator-() const {MInt res;res.x = norm(getMod() - x);return res;}constexpr MInt inv() const {assert(x != 0);return power(*this, getMod() - 2);}constexpr MInt &operator*=(MInt rhs) & {x = 1LL * x * rhs.x % getMod();return *this;}constexpr MInt &operator+=(MInt rhs) & {x = norm(x + rhs.x);return *this;}constexpr MInt &operator-=(MInt rhs) & {x = norm(x - rhs.x);return *this;}constexpr MInt &operator/=(MInt rhs) & {return *this *= rhs.inv();}friend constexpr MInt operator*(MInt lhs, MInt rhs) {MInt res = lhs;res *= rhs;return res;}friend constexpr MInt operator+(MInt lhs, MInt rhs) {MInt res = lhs;res += rhs;return res;}friend constexpr MInt operator-(MInt lhs, MInt rhs) {MInt res = lhs;res -= rhs;return res;}friend constexpr MInt operator/(MInt lhs, MInt rhs) {MInt res = lhs;res /= rhs;return res;}friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {i64 v;is >> v;a = MInt(v);return is;}friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {return os << a.val();}friend constexpr bool operator==(MInt lhs, MInt rhs) {return lhs.val() == rhs.val();}friend constexpr bool operator!=(MInt lhs, MInt rhs) {return lhs.val() != rhs.val();}
};template<>
int MInt<0>::Mod = 998244353;template<int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();constexpr int P = 998244353;
using Z = MInt<P>;template<class Info, class Tag>
struct LazySegmentTree {#define l(p) (p << 1)#define r(p) (p << 1 | 1)int n;std::vector<Info> info;std::vector<Tag> tag;LazySegmentTree() {}LazySegmentTree(int _n, Info _v = Info()) {init(_n, _v);}template<class T>LazySegmentTree(std::vector<T> _init) {init(_init);}void init(int _n, Info _v = Info()) {init(std::vector(_n, _v));}template<class T>void init(std::vector<T> _init) {n = _init.size();info.assign(4 << std::__lg(n), Info());tag.assign(4 << std::__lg(n), Tag());auto build = [&](auto self, int p, int l, int r) {if (r - l == 1) {info[p] = _init[l];return;}int m = l + r >> 1;self(self, l(p), l, m);self(self, r(p), m, r);pull(p);};build(build, 1, 0, n);}void pull(int p) {info[p] = info[l(p)] + info[r(p)];}void apply(int p, const Tag &v, int len) {info[p].apply(v, len);tag[p].apply(v);}void push(int p, int len) {apply(l(p), tag[p], len / 2);apply(r(p), tag[p], len - len / 2);tag[p] = Tag();}void modify(int p, int l, int r, int x, const Info &v) {if (r - l == 1) {info[p] = v;return;}int m = l + r >> 1;push(p, r - l);if (x < m) {modify(l(p), l, m, x, v);} else {modify(r(p), m, r, x, v);}pull(p);}void modify(int p, const Info &v) {modify(1, 0, n, p, v);}Info query(int p, int l, int r, int x, int y) {if (l >= y or r <= x) {return Info();}if (l >= x and r <= y) {return info[p];}int m = l + r >> 1;push(p, r - l);return query(l(p), l, m, x, y) + query(r(p), m, r, x, y);}Info query(int l, int r) {return query(1, 0, n, l, r);}void Apply(int p, int l, int r, int x, int y, const Tag &v) {if (l >= y or r <= x) {return;}if (l >= x and r <= y) {apply(p, v, r - l);return;}int m = l + r >> 1;push(p, r - l);Apply(l(p), l, m, x, y, v);Apply(r(p), m, r, x, y, v);pull(p);}void Apply(int l, int r, const Tag &v) {return Apply(1, 0, n, l, r, v);}template<class F>int findFirst(int p, int l, int r, int x, int y, F &&pred) {if (l >= y or r <= x) {return -1;}if (l >= x and r <= y and !pred(info[p])) {return -1;}if (r - l == 1) {return l;}push(p, r - l);int m = l + r >> 1;int res = findFirst(l(p), l, m, x, y, pred);if (res == -1) {res = findFirst(r(p), m, r, x, y, pred);}return res;}template<class F>int findFirst(int l, int r, F &&pred) {return findFirst(1, 0, n, l, r, pred);}template<class F>int findLast(int p, int l, int r, int x, int y, F &&pred) {if (l >= y or r <= x) {return -1;}if (l >= x and r <= y and !pred(info[p])) {return -1;}if (r - l == 1) {return l;}push(p, r - l);int m = l + r >> 1;int res = findLast(r(p), m, r, x, y, pred);if (res == -1) {res = findLast(l(p), l, m, x, y, pred);}return res;}template<class F>int findLast(int l, int r, F &&pred) {return findLast(1, 0, n, l, r, pred);}#undef l(p)#undef r(p)
};struct Tag {Z mul = 1;Z add = 0;void apply(const Tag &t) {mul = mul * t.mul;add = add * t.mul + t.add;}
};
struct Info {Z sum = 0;void apply(const Tag &t, int len) {sum = sum * t.mul + t.add;}
};
Info operator+(const Info &a, const Info &b) {return {a.sum + b.sum};
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n, m;std::cin >> n >> m;std::vector<int> a(n);std::cin >> a;LazySegmentTree<Info, Tag> sgt(n);for (int i = 0; i < n; i++) {sgt.modify(i, {a[i]});}while (m--) {int l, r, x;std::cin >> l >> r >> x;l--;Z p = Z(r - l).inv();sgt.Apply(l, r, {1 - p, p * x});}for (int i = 0; i < n; i++) {std::cout << sgt.query(i, i + 1).sum << " \n"[i == n - 1];}return 0;
}
I. DeepSeek
Solution
前缀和 + 二分即可。
看官解是 d e e p s e e k \rm{deepseek} deepseek 出的,感觉还挺有意思。
时间复杂度 O ( Q log N ) O(Q\log N) O(QlogN)
C++ Code
#include <bits/stdc++.h>using i64 = long long;template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;}return is;
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int N, Q;std::cin >> N >> Q;std::vector<int> R(N);std::cin >> R;std::ranges::sort(R);std::vector<i64> s(N + 1);for (int i = 0; i < N; i++) {s[i + 1] = s[i] + R[i];}while (Q--) {i64 X;std::cin >> X;int i = std::ranges::upper_bound(s, X) - s.begin() - 1;std::cout << i << "\n";}return 0;
}
J. 湖工三宝
Solution
非常明显的反悔贪心。
具体做法已经套路化:对截止时间 e e e 排序,然后如果 T + t ≤ e T + t \leq e T+t≤e,就选它;否则看大根堆里之前选的那些有没有 t ′ t' t′ 比当前的 t t t 大的,直接换掉。
官解似乎还有个 01 01 01 背包解法,感兴趣可以去看看。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)
C++ Code
#include <bits/stdc++.h>using i64 = long long;int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n;std::cin >> n;std::vector<std::array<int, 4>> job(n);for (auto &[e, a, b, c]: job) {std::cin >> a >> b >> c >> e;}std::ranges::sort(job);int T = 0;int ans = 0;std::priority_queue<int> q;for (const auto &[e, a, b, c]: job) {int t = a + b + c;if (T + t <= e) {T += t;q.push(t);ans++;continue;}if (not q.empty() and q.top() > t) {T -= q.top(); q.pop();q.push(t);T += t;}}std::cout << ans << "\n";return 0;
}
K. 在哪签到?
Solution
直接输出 H B U T \rm{HBUT} HBUT。
Python Code
print("HBUT")
L. 帮帮Carson
Solution
模拟题,直接三位三位模拟。
C++ Code
#include <bits/stdc++.h>int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::string s;std::cin >> s;int ans = std::numeric_limits<int>::max() / 2;for (int i = 0; i + 3 <= s.size(); i++) {ans = std::min(ans, std::abs(std::stoi(s.substr(i, 3)) - 753));}std::cout << ans << "\n";return 0;
}
M. 奶龙农场的宝藏计算
题目大意
给定 n n n 只奶绿,每只奶龙都有一个“可爱值”,用一个整数表示,可爱值越大越可爱。
农场主发现了一个神奇的现象:当几只奶龙聚在一起玩耍时,它们会创造出一个“快乐宝藏”,而宝藏的价值取决于这个小团体中“最可爱”的奶龙和“最不可爱”的奶龙的可爱值的乘积。
现在,农场主记录了所有奶龙的可爱值,形成了一个长度为 n n n 的序列 a a a。
他想知道,如果考虑所有可能的奶龙组合(即所有非空子序列),这些组合创造的“快乐宝藏”的总价值是多少, 最后的结果模 998244353 998244353 998244353。
数据范围
- 1 ≤ n ≤ 2 ⋅ 1 0 5 , 1 \leq n \leq 2 \cdot 10^5, 1≤n≤2⋅105,
- 0 ≤ a i < 998244353. 0 \leq a_i < 998244353. 0≤ai<998244353.
Solution
长度为 1 1 1 的区间的快乐价值总和显然是 ∑ i = 1 n a i 2 . \sum\limits_{i = 1}^{n}a_i^2. i=1∑nai2. 下面考虑区间长度 > 1 > 1 >1 的。
设 a a a 已经排好序,当前选择的最小值为 a i a_i ai,最大值为 a j a_j aj,满足 j − i + 1 > 1 j - i + 1 > 1 j−i+1>1。
那么固定最大最小值的子序列就有 2 j − i − 1 2^{j - i - 1} 2j−i−1 种选法(中间爱选不选)。
因此总的价值是 ∑ i = 1 n − 1 ∑ j = i + 1 n 2 j − i − 1 a i ⋅ a j . \sum\limits_{i = 1}^{n - 1}\sum\limits_{j = i + 1}^{n}2^{j - i - 1}a_i \cdot a_j. i=1∑n−1j=i+1∑n2j−i−1ai⋅aj.
下面开始化简。
∑ i = 1 n − 1 ∑ j = i + 1 n 2 j − i − 1 a i ⋅ a j = ∑ i = 2 n ∑ j = 1 i − 1 2 i − j − 1 a i ⋅ a j = ∑ i = 2 n a i ( ∑ j = 1 i − 1 2 ( i − 1 ) − j a j ) . \begin{align*} \sum\limits_{i = 1}^{n - 1}\sum\limits_{j = i + 1}^{n}2^{j - i - 1}a_i \cdot a_j &= \sum\limits_{i = 2}^{n}\sum\limits_{j = 1}^{i - 1}2^{i - j - 1}a_i \cdot a_j \\ &= \sum\limits_{i = 2}^{n}a_i \left(\sum\limits_{j = 1}^{i - 1}2^{(i - 1) - j}a_j\right). \end{align*} i=1∑n−1j=i+1∑n2j−i−1ai⋅aj=i=2∑nj=1∑i−12i−j−1ai⋅aj=i=2∑nai(j=1∑i−12(i−1)−jaj).
令 f ( i ) = ∑ j = 1 i − 1 2 ( i − 1 ) − j a j f(i) = \sum\limits_{j = 1}^{i - 1}2^{(i - 1) - j}a_j f(i)=j=1∑i−12(i−1)−jaj,那么
f ( i + 1 ) = ∑ j = 1 i 2 i − j a j = ( ∑ j = 1 i − 1 2 i − j a j ) + a i = 2 ( ∑ j = 1 i − 1 2 ( i − 1 ) − j a j ) + a i = 2 f ( i ) + a i . \begin{align*} f(i + 1) &= \sum\limits_{j = 1}^{i}2^{i - j}a_j \\ &= \left(\sum\limits_{j = 1}^{i - 1}2^{i - j}a_j \right) + a_i \\ &= 2 \left(\sum\limits_{j = 1}^{i - 1}2^{(i - 1) - j}a_j \right)+ a_i \\ &= 2f(i) + a_i. \end{align*} f(i+1)=j=1∑i2i−jaj=(j=1∑i−12i−jaj)+ai=2(j=1∑i−12(i−1)−jaj)+ai=2f(i)+ai.
初值条件: f ( 1 ) = a 1 f(1) = a_1 f(1)=a1。于是求总和只要 O ( n ) O(n) O(n)。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)
- 瓶颈是排序。
C++ Code
#include <bits/stdc++.h>using i64 = long long;template<class T>
constexpr T power(T a, i64 b) {T res = 1;for (; b; b /= 2, a *= a) {if (b % 2) {res *= a;}}return res;
}
template<int P>
struct MInt {int x;constexpr MInt() : x{} {}constexpr MInt(i64 x) : x{norm(x % getMod())} {}static int Mod;constexpr static int getMod() {if (P > 0) {return P;} else {return Mod;}}constexpr static void setMod(int Mod_) {Mod = Mod_;}constexpr int norm(int x) const {if (x < 0) {x += getMod();}if (x >= getMod()) {x -= getMod();}return x;}constexpr int val() const {return x;}explicit constexpr operator int() const {return x;}constexpr MInt operator-() const {MInt res;res.x = norm(getMod() - x);return res;}constexpr MInt inv() const {assert(x != 0);return power(*this, getMod() - 2);}constexpr MInt &operator*=(MInt rhs) & {x = 1LL * x * rhs.x % getMod();return *this;}constexpr MInt &operator+=(MInt rhs) & {x = norm(x + rhs.x);return *this;}constexpr MInt &operator-=(MInt rhs) & {x = norm(x - rhs.x);return *this;}constexpr MInt &operator/=(MInt rhs) & {return *this *= rhs.inv();}friend constexpr MInt operator*(MInt lhs, MInt rhs) {MInt res = lhs;res *= rhs;return res;}friend constexpr MInt operator+(MInt lhs, MInt rhs) {MInt res = lhs;res += rhs;return res;}friend constexpr MInt operator-(MInt lhs, MInt rhs) {MInt res = lhs;res -= rhs;return res;}friend constexpr MInt operator/(MInt lhs, MInt rhs) {MInt res = lhs;res /= rhs;return res;}friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {i64 v;is >> v;a = MInt(v);return is;}friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {return os << a.val();}friend constexpr bool operator==(MInt lhs, MInt rhs) {return lhs.val() == rhs.val();}friend constexpr bool operator!=(MInt lhs, MInt rhs) {return lhs.val() != rhs.val();}
};template<>
int MInt<0>::Mod = 998244353;template<int V, int P>
constexpr MInt<P> CInv = MInt<P>(V).inv();constexpr int P = 998244353;
using Z = MInt<P>;template<class T>
std::istream &operator>>(std::istream &is, std::vector<T> &v) {for (auto &x: v) {is >> x;}return is;
}int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);int n;std::cin >> n;std::vector<int> a(n);std::cin >> a;std::ranges::sort(a);Z ans = std::accumulate(a.begin(), a.end(), Z(0), [&](Z s, int x) {return s + Z(x) * x;});Z dp = a[0];for (int i = 1; i < n; i++) {ans += a[i] * dp;dp = 2 * dp + a[i];}std::cout << ans << "\n";return 0;
}