ABC127E. Cell Distance
所以, 和 可以分别单独考虑
这里仅讨论 的贡献, 是类似的
代码实现
#include <bits/stdc++.h> #include <atcoder/all> using namespace atcoder; #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using mint = modint1000000007; struct modinv { int n; vector<mint> d; modinv(): n(2), d({0,1}) {} mint operator()(int i) { while (n <= i) d.push_back(-d[mint::mod()%n]*(mint::mod()/n)), ++n; return d[i]; } mint operator[](int i) const { return d[i];} } invs; struct modfact { int n; vector<mint> d; modfact(): n(2), d({1,1}) {} mint operator()(int i) { while (n <= i) d.push_back(d.back()*n), ++n; return d[i]; } mint operator[](int i) const { return d[i];} } facts; struct modfactinv { int n; vector<mint> d; modfactinv(): n(2), d({1,1}) {} mint operator()(int i) { while (n <= i) d.push_back(d.back()*invs(n)), ++n; return d[i]; } mint operator[](int i) const { return d[i];} } ifacts; mint comb(int n, int k) { if (n < k || k < 0) return 0; return facts(n)*ifacts(k)*ifacts(n-k); } int main() { int n, m, k; cin >> n >> m >> k; mint ans; rep(i, n) ans += mint(i)*(n-i)*m*m; rep(j, m) ans += mint(j)*(m-j)*n*n; ans *= comb(n*m-2, k-2); cout << ans.val() << '\n'; return 0; }
ABC149F. Surrounded Nodes
ABC223H. Xor Query
前缀线性基的板题
对于每一位上的基底,尽可能挂越靠右的数
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using ll = long long; const int D = 60; template<typename T> struct MaxBasis { vector<T> d; vector<int> w; MaxBasis(): d(D), w(D) {} void add(T x, int nw) { rep(i, D) if (x>>i&1) { if (d[i]) { if (nw > w[i]) swap(d[i], x), swap(w[i], nw); x ^= d[i]; } else { d[i] = x; w[i] = nw; break; } } } bool solve(int l, ll x) { rep(i, D) if (x>>i&1) { if (w[i] >= l) x ^= d[i]; } return x == 0; } }; struct Q { int i, l; ll x; Q(int i, int l, ll x): i(i), l(l), x(x) {} }; int main() { cin.tie(nullptr) -> sync_with_stdio(false); int n, q; cin >> n >> q; vector<ll> a(n); rep(i, n) cin >> a[i]; vector<vector<Q>> qs(n); rep(qi, q) { int l, r; ll x; cin >> l >> r >> x; --l; --r; qs[r].emplace_back(qi, l, x); } MaxBasis<ll> mb; vector<bool> ans(q); rep(i, n) { mb.add(a[i], i); for (auto [qi, l, x] : qs[i]) { ans[qi] = mb.solve(l, x); } } rep(i, q) { if (ans[i]) puts("Yes"); else puts("No"); } return 0; }
ABC229G. Longest Y
双指针
先预处理出每个 Y
左边有多少个 .
,对于一段区间将所有的 Y
交换到一起意味着让区间里每个 Y
处左边的 .
数相同,而 “让区间里每个 Y
处左边的 .
数相同”的最小操作次数是个经典问题,就是 LC462。然后预处理一下 .
数序列的前缀和就可以做到 判定。
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using ll = long long; int main() { string s; ll k; cin >> s >> k; int n = s.size(); vector<int> a; rep(i, n) if (s[i] == 'Y') a.push_back(i-a.size()); n = a.size(); vector<ll> d(n+1); rep(i, n) d[i+1] = d[i]+a[i]; int ans = 0; int r = 0; rep(l, n) { while (r < n) { int nr = r+1; int c = (l+nr)/2; ll now = ll(c-l)*a[c] - (d[c]-d[l]); now += (d[nr]-d[c]) - ll(nr-c)*a[c]; if (now > k) break; r = nr; } ans = max(ans, r-l); } cout << ans << '\n'; return 0; }
ABC230F. Predilection
原题可以理解成对原序列划分成若干段,接着分别对每段求和,就得到了一个新的序列 ,问能生成多少种本质不同的序列
可以发现每次操作,原序列的前缀和序列 中都会少一个数(显然不会删掉 和 ),那么本题就转化成了有多少个本质不同的序列 的子序列,而这个问题是经典的dp题。
记 dp[i]
表示最后取的是数字 的本质不同的子序列个数
转移方程:
可以用 map
来维护 上一次出现的位置
这样一来时间复杂度就是
下面考虑进行优化:
可以考虑容斥
代码实现
#include <bits/stdc++.h> #include <atcoder/all> using namespace atcoder; #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using ll = long long; using mint = modint998244353; int main() { int n; cin >> n; vector<ll> a(n); rep(i, n) cin >> a[i]; vector<ll> s(n+1); rep(i, n) s[i+1] = s[i]+a[i]; s.erase(s.begin()); s.pop_back(); n--; mint ans = 1; map<ll, mint> dp; rep(i, n) { mint tmp = ans; ans += ans-dp[s[i]]; dp[s[i]] = tmp; } cout << ans.val() << '\n'; return 0; }
ABC230G. GCD Permutation
记 表示从 中任选两个数不互素的二元组的个数
那么答案就是 ,这里的 指由 中所有下标为 的倍数的数构成的子序列,
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using ll = long long; using P = pair<int, int>; struct Sieve { int n; vector<int> f, primes; Sieve(int n=1): n(n), f(n+1) { f[0] = f[1] = -1; for (ll i = 2; i <= n; ++i) { if (f[i]) continue; primes.push_back(i); f[i] = i; for (ll j = i*i; j <= n; j += i) { if (!f[j]) f[j] = i; } } } vector<int> factorList(int x) { vector<int> res; while (x != 1) { res.push_back(f[x]); x /= f[x]; } return res; } vector<P> factor(int x) { vector<int> fl = factorList(x); if (fl.size() == 0) return {}; vector<P> res(1, P(fl[0], 0)); for (int p : fl) { if (res.back().first == p) { res.back().second++; } else { res.emplace_back(p, 1); } } return res; } vector<int> divisors2(int x) { auto ps = factor(x); vector<int> res{1}; for (auto [p, _] : ps) { for (int i = res.size()-1; i >= 0; --i) { res.push_back(res[i]*p); } } res.erase(res.begin()); return res; } }; ll c2(ll n) { return n*(n+1)/2; } int main() { int n; cin >> n; Sieve prime(n); vector<int> a(n+1); rep(i, n) cin >> a[i+1]; vector<int> mu(n+1, -1); mu[1] = 0; for (int p : prime.primes) { for (int i = p; i <= n; i += p) mu[i] = -mu[i]; for (ll i = (ll)p*p; i <= n; i += (ll)p*p) mu[i] = 0; } vector<vector<int>> ds(n+1); for (int i = 1; i <= n; ++i) { ds[i] = prime.divisors2(i); } ll ans = 0; for (int k = 2; k <= n; ++k) { if (mu[k] == 0) continue; ll now = 0; unordered_map<int, int> s; for (int i = k; i <= n; i += k) { for (int x : ds[a[i]]) s[x]++; } for (auto [j, cnt] : s) now += c2(cnt)*mu[j]; ans += now*mu[k]; } cout << ans << '\n'; return 0; }
ABC233F. Swap and Sort
原题其实就是给定一张 个点 条边的图,顶点 上放有棋子 。一条边直接相连的两端点可以交换其上放置的棋子。问是否能让棋子 落在点 上。对于树的情况比较简单,可以从叶子节点 开始搜,在整棵树上找是否有哪个点上放有棋子 ,如果找到了的话,那么这个叶节点及其和它父亲相连的边就可以不考虑了。那么对于一般图的话,只需考虑每个连通块的生成树即可。
代码实现
#include <bits/stdc++.h> #if __has_include(<atcoder/all>) #include <atcoder/all> using namespace atcoder; #endif using namespace std; #define rep(i, n) for (int i = 0; i < (n); ++i) struct Edge { int to, id; Edge(int to, int id): to(to), id(id) {} }; int main() { int n; cin >> n; vector<int> P(n); rep(i, n) cin >> P[i]; rep(i, n) P[i]--; int m; cin >> m; dsu uf(n); vector<vector<Edge>> g(n); rep(i, m) { int a, b; cin >> a >> b; --a; --b; if (uf.same(a, b)) continue; uf.merge(a, b); g[a].emplace_back(b, i+1); g[b].emplace_back(a, i+1); } vector<int> ans; rep(sv, n) if (uf.leader(sv) == sv) { auto get = [&](auto& f, int v, int tg, int p=-1) -> bool { if (P[v] == tg) return true; for (auto [u, id] : g[v]) { if (u == p) continue; if (f(f, u, tg, v)) { ans.push_back(id); swap(P[v], P[u]); return true; } } return false; }; auto dfs = [&](auto& f, int v, int p=-1) -> void { for (auto [u, id] : g[v]) { if (u == p) continue; f(f, u, v); } if (!get(get, v, v)) { puts("-1"); exit(0); } }; dfs(dfs, sv); } cout << ans.size() << '\n'; for (int i : ans) cout << i << ' '; return 0; }
ABC233G. Strongest Takahashi
注意到如果能用一个子矩阵,用两个相交的子矩阵反而更劣
考虑二维区间dp
记 dp[si][sj][ti][tj]
表示摧毁左上坐标为 以及右下坐标为 的子矩阵中的所有障碍物的最小代价
代码实现
#include<bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; inline void chmin(int& x, int y) { if (x > y) x = y; } int dp[55][55][55][55]; int main() { int n; cin >> n; vector<string> s(n); rep(i, n) cin >> s[i]; rep(ti, n+1)rep(si, ti)rep(tj, n+1)rep(sj, tj) { dp[si][sj][ti][tj] = max(ti-si, tj-sj); } rep(i, n)rep(j, n) if (s[i][j] == '.') { dp[i][j][i+1][j+1] = 0; } for (int wi = 1; wi <= n; ++wi) { for (int wj = 1; wj <= n; ++wj) { rep(si, n)rep(sj, n) { int ti = si+wi, tj = sj+wj; if (ti > n) break; if (tj > n) break; for (int k = si+1; k < ti; ++k) { int now = dp[si][sj][k][tj]; now += dp[k][sj][ti][tj]; chmin(dp[si][sj][ti][tj], now); } for (int k = sj+1; k < tj; ++k) { int now = dp[si][sj][ti][k]; now += dp[si][k][ti][tj]; chmin(dp[si][sj][ti][tj], now); } } } } cout << dp[0][0][n][n] << '\n'; return 0; }
ABC233H. Manhattan Christmas Tree
整体二分
先将坐标系顺时针旋转45°,得到切比雪夫距离,接下来就是经典的二维数点问题了
代码实现
#include <bits/stdc++.h> #if __has_include(<atcoder/all>) #include <atcoder/all> using namespace atcoder; #endif #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using P = pair<int, int>; const int M = 100005; const int MX = M*2; int main() { int n; cin >> n; vector<vector<int>> ps(MX); rep(i, n) { int x, y; cin >> x >> y; ps[x+y].push_back(x-y+M); } int q; cin >> q; vector<int> a(q), b(q), k(q); rep(i, q) { int x, y; cin >> x >> y >> k[i]; a[i] = x+y; b[i] = x-y+M; } vector<int> wa(q, -1), ac(q, MX); rep(ti, 18) { vector<int> num(q), wj(q); vector<vector<int>> qs(MX); rep(i, q) { wj[i] = (wa[i]+ac[i])/2; int lx = a[i]-wj[i], rx = a[i]+wj[i]+1; lx = max(lx, 0); rx = min(rx, MX-1); qs[lx].push_back(i); qs[rx].push_back(q+i); } fenwick_tree<int> d(MX); rep(x, MX) { for (int qi : qs[x]) { int i = qi%q; int sign = qi < q ? -1 : 1; int ly = b[i]-wj[i], ry = b[i]+wj[i]+1; ly = max(ly, 0); ry = min(ry, MX); num[i] += d.sum(ly, ry)*sign; } for (int y : ps[x]) d.add(y, 1); } rep(i, q) { if (num[i] >= k[i]) ac[i] = wj[i]; else wa[i] = wj[i]; } } rep(i, q) cout << ac[i] << '\n'; return 0; }
ABC235G. Gardens
先考虑条件1,就是个简单的容斥,见 盒子与球
再结合条件2,假设恰有 个花园对条件1没有限制,方案数就是
那么最终答案就是
这样时间复杂度是
考虑对组合数的前缀和进行加速
记
有递推公式:
那么我们就可以 预处理出 了
代码实现
#include <bits/stdc++.h> #include <atcoder/all> using namespace atcoder; #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using mint = modint998244353; struct modinv { int n; vector<mint> d; modinv(): n(2), d({0,1}) {} mint operator()(int i) { while (n <= i) d.push_back(-d[mint::mod()%n]*(mint::mod()/n)), ++n; return d[i]; } mint operator[](int i) const { return d[i];} } invs; struct modfact { int n; vector<mint> d; modfact(): n(2), d({1,1}) {} mint operator()(int i) { while (n <= i) d.push_back(d.back()*n), ++n; return d[i]; } mint operator[](int i) const { return d[i];} } facts; struct modfactinv { int n; vector<mint> d; modfactinv(): n(2), d({1,1}) {} mint operator()(int i) { while (n <= i) d.push_back(d.back()*invs(n)), ++n; return d[i]; } mint operator[](int i) const { return d[i];} } ifacts; mint comb(int n, int k) { if (n < k || k < 0) return 0; return facts(n)*ifacts(k)*ifacts(n-k); } int main() { int n; cin >> n; vector<int> a(3); rep(i, 3) cin >> a[i]; vector f(3, vector<mint>(n+1)); rep(i, 3) { f[i][0] = 1; rep(j, n) { f[i][j+1] = f[i][j]*2; if (j >= a[i]) f[i][j+1] -= comb(j, a[i]); } } mint ans; rep(j, n+1) { mint now = 1; rep(i, 3) now *= f[i][j]; now *= comb(n, j); ans = now-ans; } cout << ans.val() << '\n'; return 0; }
ABC249F. Ignore Operations
考虑枚举最后一个操作 ,那么前面的操作不管是啥样都无所谓了,我们只需考虑后面的操作,显然应该将后面的所有操作 都跳过,为了使得最终的结果最大,所以应该尽可能跳过操作 中 的操作。可以用小根堆来维护所有 的操作 ,如果当前堆的大小超过剩下的 ,就弹出堆中的最大值。 对于枚举最后一个操作 可以倒着枚举所有操作
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using ll = long long; int main() { int n, k; cin >> n >> k; vector<int> t(n), y(n); rep(i, n) cin >> t[i] >> y[i]; ll ans = -1e18; ll sum = 0; priority_queue<int> q; for (int i = n-1; i >= 0; --i) { if (k < 0) break; if (t[i] == 1) { ans = max(ans, y[i]+sum); k--; if (q.size() > k) { sum += q.top(); q.pop(); } } else { if (y[i] >= 0) sum += y[i]; else { q.push(y[i]); if (q.size() > k) { sum += q.top(); q.pop(); } } } } if (k >= 0) ans = max(ans, sum); cout << ans << '\n'; return 0; }
ABC239Ex. Dice Product 2
记 dp[i]
表示由 通过除以若干个整数 变成 的期望次数
转移方程:
时间复杂度为
注意到和 一样的部分可以用整除分块来优化
用记忆化搜索实现,复杂度同杜教筛,为
代码实现
#include <bits/stdc++.h> #if __has_include(<atcoder/all>) #include <atcoder/all> using namespace atcoder; #endif #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using mint = modint1000000007; int main() { int n, m; cin >> n >> m; mint inv_n1 = mint(n-1).inv(); mint cost = mint(n)/(n-1); unordered_map<int, mint> dp; auto f = [&](auto& f, int x) -> mint { if (x == 0) return 0; if (dp.count(x)) return dp[x]; mint res; for (int i = n; i > 1;) { int a = x/i; int ni = x/(a+1); res += f(f, a)*(i-ni); i = ni; } res *= inv_n1; res += cost; return dp[x] = res; }; mint ans = f(f, m); cout << ans.val() << '\n'; return 0; }
ABC265G. 012 Inversion
延迟线段树
每个点需要维护以下信息:
- 的个数
- 有序对 , , ,,,,,, 的个数
对于操作二其实就是延迟线段树里的映射
代码实现
#include <bits/stdc++.h> #if __has_include(<atcoder/all>) #include <atcoder/all> using namespace atcoder; #endif #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using ll = long long; struct S { array<int, 3> c; array<array<ll, 3>, 3> d; S() { fill(c.begin(), c.end(), 0); rep(i, 3)rep(j, 3) d[i][j] = 0; } }; S op(S a, S b) { rep(i, 3)rep(j, 3) { a.d[i][j] += b.d[i][j]; a.d[i][j] += (ll)a.c[i]*b.c[j]; } rep(i, 3) a.c[i] += b.c[i]; return a; } S e() { return S(); } struct F { array<int, 3> a; F(): a({0, 1, 2}) {} }; S mapping(F f, S x) { S res; rep(i, 3)rep(j, 3) { res.d[f.a[i]][f.a[j]] += x.d[i][j]; } rep(i, 3) res.c[f.a[i]] += x.c[i]; return res; } F comp(F f2, F f1) { rep(i, 3) f1.a[i] = f2.a[f1.a[i]]; return f1; } F id() { return F(); } int main() { cin.tie(nullptr) -> sync_with_stdio(false); int n, q; cin >> n >> q; vector<int> a(n); rep(i, n) cin >> a[i]; lazy_segtree<S, op, e, F, mapping, comp, id> t(n); rep(i, n) { S s; s.c[a[i]] = 1; t.set(i, s); } rep(qi, q) { int type, l, r; cin >> type >> l >> r; --l; if (type == 1) { S s = t.prod(l, r); ll ans = s.d[1][0] + s.d[2][0] + s.d[2][1]; cout << ans << '\n'; } else { F f; rep(i, 3) cin >> f.a[i]; t.apply(l, r, f); } } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】