AtCoder Beginner Contest 378 题解
AtCoder Beginner Contest 378
Rated: \(760 \to 770\)
场切:ABCD
赛后:EF
不会:G
-
A - Pairing
简单模拟。
-
B - Garbage Collection
找到一个大于等于 \(d\) 的最小值,满足模 $q = r $。
简单分讨。 -
C - Repeating
简单模拟。
-
D - Count Simple Paths
纯DFS。
枚举每个起点,暴力搜索统计答案。 -
E - Mod Sigma Problem
假设 \(S_i = (A_1+A_2+\dots+A_i) \mathbin{\mathrm{mod}} M,S_0 = 0\),那么
\[ \sum_{1 \leq l \leq r \leq N} \left( \left(\sum_{l \leq i \leq r} A_i\right) \mathbin{\mathrm{mod}} M \right) = \sum_{1 \leq l \leq r \leq N} (S_r - S_{l-1}) \mathbin{\mathrm{mod}} M, \]由于 \(0 \leq S_{l-1},S_r \lt M\) ,所以
\[ (S_r - S_{l-1}) \mathbin{\mathrm{mod}} M = S_r - S_{l-1} + \begin{cases} 0 & (S_{l-1} \leq S_r) \\ M & (S_{l-1} \gt S_r)\end{cases} \]不再涉及 \(\mathrm{mod}\) 。
让 $X_r = $ ( \(l=1,2,\dots,r\) 与 \(S_{l-1} \gt S_r\) 的个数),那么
\[ \sum_{r=1}^N \sum_{l=1}^r (S_r - S_{l-1}) \mathbin{\mathrm{mod}} M = \sum_{r=1}^N \left( S_r \times r - \sum_{l=1}^r S_{l-1} + M \times X_r \right). \]\(X_r\) 可以用值域树状数组或值域线段树来计算。
\(B_x\) 定义为 \(S_{l-1}=x\) 的个数,
对每个 \(r=0,1,\dots,N\) 执行以下操作:- \(X_r = B_{S_r + 1} + B_{S_r + 2} + \dots B_{M-1}\)
- \(B_{S_r} + 1\)
细节:当 \(S_i = 0\) 时,执行
add(S[i],1)
时,lowbit(S[i]) 会一直为 \(0\),会死循环。所以给每个 \(S_i + 1\) 维护。点击查看代码
#include <bits/stdc++.h> using namespace std; const int N = 200005; long long a[N],tr[N]; long long n,m; int lowbit(int x){ return x & -x; } void add(int x,int k){ for (int i = x; i <= m;i+=lowbit(i)) tr[i] += k; } int query(int x){ long long sum = 0; for (int i = x; i;i-=lowbit(i)) sum += tr[i]; return sum; } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin >> n >> m; for (int i = 1; i <= n;i++) cin >> a[i], a[i] = (a[i]+a[i - 1])%m; long long tmp = 0,ans=0; for (int i = 1; i <= n;i++){ ans += a[i] * i; ans -= tmp; ans += (query(m) - query(a[i]+1))*m; add(a[i]+1, 1); tmp += a[i]; } cout << ans << endl; return 0; }
-
F - Add One Edge 2
计算满足以下条件的顶点对 \((u, v)\) (其中 \(u \lt v\) )的个数:
- \(u\) 和 \(v\) 的度数都是 \(2\) 。
- \(u\) 和 \(v\) 的简单路径上的所有顶点(不包括 \(u\) 和 \(v\) 本身)的度数都是 \(3\)。
并查集维护,给每个顶点度数都为 \(3\) 的边合并。
设 \(c_i\) 表示第 \(i\) 个连通块中,与联通块中的点有连边的,且度数为 \(2\) 的点的数量。答案就是 \(\displaystyle\sum_{i=1}^{} \frac{c_i(c_i - 1)}{2}\) 。
点击查看代码
#include <bits/stdc++.h> using namespace std; const int N = 200005; int n; int u[N], v[N], deg[N], fa[N]; int c2[N]; int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } void merge(int x, int y) { int fx = find(x),fy = find(y); fa[fx] = fy; } vector<vector<int>> f() { unordered_map<int, vector<int>> t; for (int i = 0; i < n; ++i) { int rt = find(i); t[rt].push_back(i); } vector<vector<int>> ans; for (const auto& tmp : t) { ans.push_back(tmp.second); } return ans; } signed main() { cin >> n; for (int i = 1; i <= n - 1; i++) { cin >> u[i] >> v[i]; u[i]--;v[i]--; deg[u[i]]++;deg[v[i]]++; } for (int i = 0; i < n; ++i) fa[i] = i; for (int i = 1; i <= n - 1; ++i) { if (deg[u[i]] == 3 && deg[v[i]] == 3) merge(u[i], v[i]); else if (deg[u[i]] == 3 && deg[v[i]] == 2) c2[u[i]]++; else if (deg[u[i]] == 2 && deg[v[i]] == 3) c2[v[i]]++; } long long ans = 0; for (const auto& g : f()) { long long cnt = 0; for (int v : g) cnt += c2[v]; ans += cnt * (cnt - 1) / 2; } cout << ans << endl; return 0; }
-
G - Everlasting LIDS