AtCoder Beginner Contest 352
A - AtCoder Line (abc352 A)
题目大意
给定,问 是否在之间。
解题思路
如果,则交换 ,然后判断是否有 即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, x, y, z; cin >> n >> x >> y >> z; if (x > y) swap(x, y); if (x <= z && z <= y) cout << "Yes" << '\n'; else cout << "No" << '\n'; return 0; }
B - Typing (abc352 B)
题目大意
给定字符串。 是 的子序列。现就近匹配,找到 的每个字符就近匹配到的 中的每个下标。
解题思路
按照就近匹配原则,匹配一次子序列即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); string s, t; cin >> s >> t; auto pos = t.begin(); for (auto c : s) { while (pos != t.end() && *pos != c) { pos = next(pos); } cout << (pos - t.begin() + 1) << ' '; pos = next(pos); } cout << '\n'; return 0; }
C - Standing On The Shoulders (abc352 C)
题目大意
给定个巨人,巨人有肩高和头高。
现依次放置每个巨人,每个巨人放在上一个巨人的肩上。
问最高的巨人的头高有多高。
解题思路
枚举最后的巨人是哪个,那么其头高就是。
所有可能的情况取最大值即为答案。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> a(n), b(n); for (int i = 0; i < n; i++) cin >> a[i] >> b[i]; LL suma = accumulate(a.begin(), a.end(), 0LL); LL ans = 0; for (int i = 0; i < n; i++) { ans = max(suma - a[i] + b[i], ans); } cout << ans << '\n'; return 0; }
D - Permutation Subsequence (abc352 D)
题目大意
给定一个排列和一个数。
找出个下标,最小化下标极差,使得对应的组成的集合是一个连续的 个数的集合。
解题思路
记 表示数 在原排列的位置。枚举这连续的个数,比如是,那此时的极差就是
对所有的取最小值,就是答案。
按顺序枚举,此时就是求一个长度为 的 区间的最大值和最小值,即经典的滑动窗口问题,用单调维护即可。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, k; cin >> n >> k; vector<int> pos(n); for (int i = 0; i < n; i++) { int x; cin >> x; --x; pos[x] = i; } int ans = n + 1; deque<int> minn, maxx; for (int i = 0; i < n; i++) { while (!minn.empty() && i - minn.front() >= k) { minn.pop_front(); } while (!maxx.empty() && i - maxx.front() >= k) { maxx.pop_front(); } while (!minn.empty() && pos[i] <= pos[minn.back()]) { minn.pop_back(); } while (!maxx.empty() && pos[i] >= pos[maxx.back()]) { maxx.pop_back(); } minn.push_back(i); maxx.push_back(i); if (i >= k - 1) ans = min(ans, pos[maxx.front()] - pos[minn.front()]); } cout << ans << '\n'; return 0; }
E - Clique Connect (abc352 E)
题目大意
给定一张无向图,初始无边。
现依次进行次操作,每次操作给定 个点 和边权 ,将这 个点俩俩 连边,边权为。
问最后图是否连通,若连通,问最小生成树的权值。
解题思路
用并查集维护连通性,对于每次操作,就依次合并相邻两点即可。那么总的复杂度是。考虑如何求最小生成树。
参考算法,直接从边权最小的开始连边,连边就相连即可,如果它们未相连的话。这个过程可以在并查集合并时顺便做了。
因此时间复杂度就是。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; class dsu { public: vector<int> p; vector<int> sz; int n; dsu(int _n) : n(_n) { p.resize(n); sz.resize(n); iota(p.begin(), p.end(), 0); fill(sz.begin(), sz.end(), 1); } inline int get(int x) { return (x == p[x] ? x : (p[x] = get(p[x]))); } inline bool unite(int x, int y) { x = get(x); y = get(y); if (x != y) { p[x] = y; sz[y] += sz[x]; return true; } return false; } }; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, m; cin >> n >> m; vector<pair<vector<int>, int>> a(m); for (auto& [p, c] : a) { int k; cin >> k >> c; p.resize(k); for (auto& i : p) { cin >> i; --i; } } vector<int> id(m); iota(id.begin(), id.end(), 0); sort(id.begin(), id.end(), [&](int i, int j) { return a[i].second < a[j].second; }); dsu d(n); LL ans = 0; for (auto& i : id) { auto& [p, c] = a[i]; for (auto j : p) { if (d.unite(p[0], j)) { ans += c; } } } if (d.sz[d.get(0)] != n) { ans = -1; } cout << ans << '\n'; return 0; }
F - Estimate Order (abc352 F)
题目大意
个人,排名唯一,给定关于这些人的条排名信息,问每个人的名次是否能唯一确定。
信息为,表示第个人的名次 与第 个人的名字 的差。
解题思路
考虑朴素做法怎么做。
个人,每条信息就给两个人连边,然后就得到有若干个连通块。
对于每个连通块,如果我指定其中一个人的排名,那该连通块的其他人的排名都唯一确定了。
朴素的做法就是依次指定每个连通块中任意一个人的排名,然后求得该连通块其他人的排名,没有冲突的话,就继续指定下一个连通块的任意一个人的排名。如果存在合法的排名,则记录下来。最后看每个连通块合法的排名的个数是否是 ,是则唯一确定,否则不唯一。
朴素做法的时间复杂度 ,这里可以加点优化,比如只有一个点的我们不考虑,那么最多只有个连通块,则此时的复杂度就是 ,有 。
考虑优化,在搜索时我们保留了每个数字用在了哪个连通块上,因此其状态数是一个阶乘,但事实上这个状态是冗余的——我们只需要知道用了哪些数字,而不需要这些数字用在了哪些连通块上。
考虑转移,对当前连通块,我们需要枚举一个没用过的数字,然后求该连通块每个人的排名,看看排名是否唯一。也就是说,我们需要的状态仅仅只是数字的是否使用,就可以做到转移,而不需要知道数字用在了哪里,由此状态数可以从阶乘级别降到指数级别。
然后,再记忆化搜索一下就解决了。
即设表示当前使用的数字状态是, 剩余连通块填剩余数字,是否存在可行解。转移就枚举该连通块的一个数字,然后下得到整个连通块需要用到的数字,没冲突的话,如果可行,则说明当前连通块填该数字是合法的,记录一下。
最后看每个连通块的合法数字个数是否唯一,是则能唯一确定。
这个的设计方式其实就是朴素的记忆化搜索,跟递归形式的 数位 很类似。
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n, m; cin >> n >> m; vector<vector<array<int, 2>>> edge(n); for (int i = 0; i < m; i++) { int a, b, c; cin >> a >> b >> c; --a, --b; edge[a].push_back({b, -c}); edge[b].push_back({a, c}); } int up = (1 << n); vector<int> used(n, 0); vector<int> st; auto bfs1 = [&](int st) { queue<int> team; team.push(st); used[st] = 1; while (!team.empty()) { int u = team.front(); team.pop(); for (auto& [v, c] : edge[u]) { if (!used[v]) { used[v] = 1; team.push(v); } } } }; for (int i = 0; i < n; ++i) { if (used[i]) { continue; } st.push_back(i); bfs1(i); } vector<int> dp(up, -1); vector<set<int>> candidate(st.size()); dp.back() = 1; auto bfs2 = [&](int st, int sc) -> array<int, 2> { int ret = 0; queue<array<int, 2>> team; vector<int> val(n, -1); team.push({st, sc}); val[st] = sc; ret |= (1 << sc); int sz = 0; while (!team.empty()) { auto [u, c] = team.front(); team.pop(); sz++; for (auto& [v, w] : edge[u]) { int nc = c + w; if (val[v] == -1) { if (nc < 0 || nc >= n || (ret >> nc) & 1) { return {false, 0}; } ret |= (1 << nc); val[v] = nc; team.push({v, nc}); } else if (val[v] != nc) { return {false, 0}; } } } assert(sz == __builtin_popcount(ret)); return {true, ret}; }; auto dfs = [&](auto&& dfs, int state, int pos) -> int { if (dp[state] != -1) return dp[state]; bool ok = false; int start = st[pos]; for (int i = 0; i < n; ++i) { auto [valid, nxt] = bfs2(start, i); if (valid && ((state & nxt) == 0) && dfs(dfs, state | nxt, pos + 1)) { ok = true; candidate[pos].insert(i); } } return dp[state] = ok; }; dfs(dfs, 0, 0); vector<int> ans(n, -2); auto bfs3 = [&](int st, int w) { queue<array<int, 2>> team; team.push({st, w}); ans[st] = w; while (!team.empty()) { auto [u, c] = team.front(); team.pop(); for (auto& [v, w] : edge[u]) { int nc = c + w; if (ans[v] == -2) { ans[v] = nc; team.push({v, nc}); } } } }; for (int i = 0; i < st.size(); ++i) { if (candidate[i].size() == 1) { bfs3(st[i], *candidate[i].begin()); } } for (int i = 0; i < n; ++i) { cout << ans[i] + 1 << " \n"[i == n - 1]; } return 0; }
G - Socks 3 (abc352 G)
题目大意
抽屉里有个颜色的袜子,各有 只。
现每次从抽屉里等概率取一只袜子,不放回,直到取出两只颜色相同的袜子为止。
问取出的袜子的期望只数。
解题思路
假设恰好取出只袜子后,有两只颜色相同的概率是 ,那么期望次数即为 。(这个式子比较经典,是一个后缀和的后缀和的展开式。)
假设取出只或以上后,有两只颜色相同的概率是 ,即 ,是个后缀和,则 。代替上述期望式子得 。
表示需要取超过只袜子才能有两只袜子颜色相同的概率,换句话说,它就是取只袜子,没有两只颜色相同的概率(这才需要取只或以上才能有两只袜子颜色相同,它不意味着第 只能取到颜色相同)。
而取只颜色各不相同的概率,可先枚举对应的 种颜色,然后。从生成函数的角度考虑,每种颜色的生成函数是 ,则所有颜色的生成函数相乘 的结果的 的系数就是上述的分子,分母(总情况数)就是 ,。
因此需要求出 的结果,朴素的乘法需要进行次,每次用 优化的话,总复杂度也得是 。
因为每次相乘,多项式的最高幂才 ,可以考虑分治,假设,要求,则先求 和 ,再两者相乘,就得到 ,而 和 就递归求解。这样每次相乘,多项式的最高幂就会翻倍,最多翻次就得到结果,而不是之前的次。即总的时间复杂度是 。
代码是贴了的 板子和取模板子。
(感觉转换部分不能一眼看透,上面说的感觉不像是本质
神奇的代码
#include <bits/stdc++.h> using namespace std; using LL = long long; template <typename T> T inverse(T a, T m) { T u = 0, v = 1; while (a != 0) { T t = m / a; m -= t * a; swap(a, m); u -= t * v; swap(u, v); } assert(m == 1); return u; } template <typename T> class Modular { public: using Type = typename decay<decltype(T::value)>::type; constexpr Modular() : value() {} template <typename U> Modular(const U& x) { value = normalize(x); } template <typename U> static Type normalize(const U& x) { Type v; if (-mod() <= x && x < mod()) v = static_cast<Type>(x); else v = static_cast<Type>(x % mod()); if (v < 0) v += mod(); return v; } const Type& operator()() const { return value; } template <typename U> explicit operator U() const { return static_cast<U>(value); } constexpr static Type mod() { return T::value; } Modular& operator+=(const Modular& other) { if ((value += other.value) >= mod()) value -= mod(); return *this; } Modular& operator-=(const Modular& other) { if ((value -= other.value) < 0) value += mod(); return *this; } template <typename U> Modular& operator+=(const U& other) { return *this += Modular(other); } template <typename U> Modular& operator-=(const U& other) { return *this -= Modular(other); } Modular& operator++() { return *this += 1; } Modular& operator--() { return *this -= 1; } Modular operator++(int) { Modular result(*this); *this += 1; return result; } Modular operator--(int) { Modular result(*this); *this -= 1; return result; } Modular operator-() const { return Modular(-value); } template <typename U = T> typename enable_if<is_same<typename Modular<U>::Type, int>::value, Modular>::type& operator*=(const Modular& rhs) { #ifdef _WIN32 uint64_t x = static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value); uint32_t xh = static_cast<uint32_t>(x >> 32), xl = static_cast<uint32_t>(x), d, m; asm("divl %4; \n\t" : "=a"(d), "=d"(m) : "d"(xh), "a"(xl), "r"(mod())); value = m; #else value = normalize(static_cast<int64_t>(value) * static_cast<int64_t>(rhs.value)); #endif return *this; } template <typename U = T> typename enable_if<is_same<typename Modular<U>::Type, int64_t>::value, Modular>::type& operator*=(const Modular& rhs) { int64_t q = static_cast<int64_t>(static_cast<long double>(value) * rhs.value / mod()); value = normalize(value * rhs.value - q * mod()); return *this; } template <typename U = T> typename enable_if<!is_integral<typename Modular<U>::Type>::value, Modular>::type& operator*=(const Modular& rhs) { value = normalize(value * rhs.value); return *this; } Modular& operator/=(const Modular& other) { return *this *= Modular(inverse(other.value, mod())); } template <typename U> friend bool operator==(const Modular<U>& lhs, const Modular<U>& rhs); template <typename U> friend bool operator<(const Modular<U>& lhs, const Modular<U>& rhs); template <typename U> friend std::istream& operator>>(std::istream& stream, Modular<U>& number); private: Type value; }; template <typename T> bool operator==(const Modular<T>& lhs, const Modular<T>& rhs) { return lhs.value == rhs.value; } template <typename T, typename U> bool operator==(const Modular<T>& lhs, U rhs) { return lhs == Modular<T>(rhs); } template <typename T, typename U> bool operator==(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) == rhs; } template <typename T> bool operator!=(const Modular<T>& lhs, const Modular<T>& rhs) { return !(lhs == rhs); } template <typename T, typename U> bool operator!=(const Modular<T>& lhs, U rhs) { return !(lhs == rhs); } template <typename T, typename U> bool operator!=(U lhs, const Modular<T>& rhs) { return !(lhs == rhs); } template <typename T> bool operator<(const Modular<T>& lhs, const Modular<T>& rhs) { return lhs.value < rhs.value; } template <typename T> Modular<T> operator+(const Modular<T>& lhs, const Modular<T>& rhs) { return Modular<T>(lhs) += rhs; } template <typename T, typename U> Modular<T> operator+(const Modular<T>& lhs, U rhs) { return Modular<T>(lhs) += rhs; } template <typename T, typename U> Modular<T> operator+(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) += rhs; } template <typename T> Modular<T> operator-(const Modular<T>& lhs, const Modular<T>& rhs) { return Modular<T>(lhs) -= rhs; } template <typename T, typename U> Modular<T> operator-(const Modular<T>& lhs, U rhs) { return Modular<T>(lhs) -= rhs; } template <typename T, typename U> Modular<T> operator-(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) -= rhs; } template <typename T> Modular<T> operator*(const Modular<T>& lhs, const Modular<T>& rhs) { return Modular<T>(lhs) *= rhs; } template <typename T, typename U> Modular<T> operator*(const Modular<T>& lhs, U rhs) { return Modular<T>(lhs) *= rhs; } template <typename T, typename U> Modular<T> operator*(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) *= rhs; } template <typename T> Modular<T> operator/(const Modular<T>& lhs, const Modular<T>& rhs) { return Modular<T>(lhs) /= rhs; } template <typename T, typename U> Modular<T> operator/(const Modular<T>& lhs, U rhs) { return Modular<T>(lhs) /= rhs; } template <typename T, typename U> Modular<T> operator/(U lhs, const Modular<T>& rhs) { return Modular<T>(lhs) /= rhs; } template <typename T, typename U> Modular<T> power(const Modular<T>& a, const U& b) { assert(b >= 0); Modular<T> x = a, res = 1; U p = b; while (p > 0) { if (p & 1) res *= x; x *= x; p >>= 1; } return res; } template <typename T> string to_string(const Modular<T>& number) { return to_string(number()); } template <typename T> std::ostream& operator<<(std::ostream& stream, const Modular<T>& number) { return stream << number(); } template <typename T> std::istream& operator>>(std::istream& stream, Modular<T>& number) { typename common_type<typename Modular<T>::Type, int64_t>::type x; stream >> x; number.value = Modular<T>::normalize(x); return stream; } constexpr int md = 998244353; using Mint = Modular<std::integral_constant<decay<decltype(md)>::type, md>>; template <typename T> class NTT { public: using Type = typename decay<decltype(T::value)>::type; static Type md; static Modular<T> root; static int base; static int max_base; static vector<Modular<T>> roots; static vector<int> rev; static void clear() { root = 0; base = 0; max_base = 0; roots.clear(); rev.clear(); } static void init() { md = T::value; assert(md >= 3 && md % 2 == 1); auto tmp = md - 1; max_base = 0; while (tmp % 2 == 0) { tmp /= 2; max_base++; } root = 2; while (power(root, (md - 1) >> 1) == 1) { root++; } assert(power(root, md - 1) == 1); root = power(root, (md - 1) >> max_base); base = 1; rev = {0, 1}; roots = {0, 1}; } static void ensure_base(int nbase) { if (md != T::value) { clear(); } if (roots.empty()) { init(); } if (nbase <= base) { return; } assert(nbase <= max_base); rev.resize(1 << nbase); for (int i = 0; i < (1 << nbase); i++) { rev[i] = (rev[i >> 1] >> 1) + ((i & 1) << (nbase - 1)); } roots.resize(1 << nbase); while (base < nbase) { Modular<T> z = power(root, 1 << (max_base - 1 - base)); for (int i = 1 << (base - 1); i < (1 << base); i++) { roots[i << 1] = roots[i]; roots[(i << 1) + 1] = roots[i] * z; } base++; } } static void fft(vector<Modular<T>>& a) { int n = (int)a.size(); assert((n & (n - 1)) == 0); int zeros = __builtin_ctz(n); ensure_base(zeros); int shift = base - zeros; for (int i = 0; i < n; i++) { if (i < (rev[i] >> shift)) { swap(a[i], a[rev[i] >> shift]); } } for (int k = 1; k < n; k <<= 1) { for (int i = 0; i < n; i += 2 * k) { for (int j = 0; j < k; j++) { Modular<T> x = a[i + j]; Modular<T> y = a[i + j + k] * roots[j + k]; a[i + j] = x + y; a[i + j + k] = x - y; } } } } static vector<Modular<T>> multiply(vector<Modular<T>> a, vector<Modular<T>> b) { if (a.empty() || b.empty()) { return {}; } int eq = (a == b); int need = (int)a.size() + (int)b.size() - 1; int nbase = 0; while ((1 << nbase) < need) nbase++; ensure_base(nbase); int sz = 1 << nbase; a.resize(sz); b.resize(sz); fft(a); if (eq) b = a; else fft(b); Modular<T> inv_sz = 1 / static_cast<Modular<T>>(sz); for (int i = 0; i < sz; i++) { a[i] *= b[i] * inv_sz; } reverse(a.begin() + 1, a.end()); fft(a); a.resize(need); return a; } }; template <typename T> typename NTT<T>::Type NTT<T>::md; template <typename T> Modular<T> NTT<T>::root; template <typename T> int NTT<T>::base; template <typename T> int NTT<T>::max_base; template <typename T> vector<Modular<T>> NTT<T>::roots; template <typename T> vector<int> NTT<T>::rev; template <typename T> vector<Modular<T>> inverse(const vector<Modular<T>>& a) { assert(!a.empty()); int n = (int)a.size(); vector<Modular<T>> b = {1 / a[0]}; while ((int)b.size() < n) { vector<Modular<T>> x(a.begin(), a.begin() + min(a.size(), b.size() << 1)); x.resize(b.size() << 1); b.resize(b.size() << 1); vector<Modular<T>> c = b; NTT<T>::fft(c); NTT<T>::fft(x); Modular<T> inv = 1 / static_cast<Modular<T>>((int)x.size()); for (int i = 0; i < (int)x.size(); i++) { x[i] *= c[i] * inv; } reverse(x.begin() + 1, x.end()); NTT<T>::fft(x); rotate(x.begin(), x.begin() + (x.size() >> 1), x.end()); fill(x.begin() + (x.size() >> 1), x.end(), 0); NTT<T>::fft(x); for (int i = 0; i < (int)x.size(); i++) { x[i] *= c[i] * inv; } reverse(x.begin() + 1, x.end()); NTT<T>::fft(x); for (int i = 0; i < ((int)x.size() >> 1); i++) { b[i + ((int)x.size() >> 1)] = -x[i]; } } b.resize(n); return b; } template <typename T> vector<Modular<T>> inverse_old(vector<Modular<T>> a) { assert(!a.empty()); int n = (int)a.size(); if (n == 1) { return {1 / a[0]}; } int m = (n + 1) >> 1; vector<Modular<T>> b = inverse(vector<Modular<T>>(a.begin(), a.begin() + m)); int need = n << 1; int nbase = 0; while ((1 << nbase) < need) { ++nbase; } NTT<T>::ensure_base(nbase); int size = 1 << nbase; a.resize(size); b.resize(size); NTT<T>::fft(a); NTT<T>::fft(b); Modular<T> inv = 1 / static_cast<Modular<T>>(size); for (int i = 0; i < size; ++i) { a[i] = (2 - a[i] * b[i]) * b[i] * inv; } reverse(a.begin() + 1, a.end()); NTT<T>::fft(a); a.resize(n); return a; } template <typename T> vector<Modular<T>> operator*(const vector<Modular<T>>& a, const vector<Modular<T>>& b) { if (a.empty() || b.empty()) { return {}; } if (min(a.size(), b.size()) < 150) { vector<Modular<T>> c(a.size() + b.size() - 1, 0); for (int i = 0; i < (int)a.size(); i++) { for (int j = 0; j < (int)b.size(); j++) { c[i + j] += a[i] * b[j]; } } return c; } return NTT<T>::multiply(a, b); } template <typename T> vector<Modular<T>>& operator*=(vector<Modular<T>>& a, const vector<Modular<T>>& b) { return a = a * b; } int main(void) { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; vector<int> a(n); for (auto& x : a) cin >> x; int tot = accumulate(a.begin(), a.end(), 0); auto solve = [&](auto solve, int l, int r) -> vector<Mint> { if (l + 1 == r) return {1, a[l]}; int m = (l + r) >> 1; return solve(solve, l, m) * solve(solve, m, r); }; vector<Mint> poly = solve(solve, 0, n); Mint deno = 1, ans = 0; for (int i = 0; i <= n; i++) { ans += poly[i] / deno; deno *= tot - i; deno /= i + 1; } cout << ans << '\n'; return 0; }
本文作者:~Lanly~
本文链接:https://www.cnblogs.com/Lanly/p/18173063
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步