Codeforces Round 970 (Div. 3)
Codeforces Round 970 (Div. 3)
A. Sakurako's Exam
思路
直接枚举即可。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int a, b; cin >> a >> b; for (int i = 0; i <= a; i++) { for (int j = 0; j <= b; j++) { if (i + j * 2 == (a - i) + (b - j) * 2) { cout << "YES\n"; return ; } } } cout << "NO\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
B. Square or Not
思路
首先判断一下 \(n\) 是不是完全平方数,然后就看第一行和最后一行是不是全 \(1\),中间的 \(2\sim \sqrt{n}-1\) 行是不是 \(100\dots001\) 这种。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n; cin >> n; string s; cin >> s; int m = sqrt(n); if (m * m != n) { cout << "No\n"; return ; } string a = string(m, '1'), b = "1" + string(m - 2, '0') + "1"; if (s.substr(0, m) != a || s.substr(n - m) != a) { cout << "No\n"; return ; } for (int i = m; i < n - m; i += m) { if (s.substr(i, m) != b) { cout << "No\n"; return ; } } cout << "Yes\n"; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
C. Longest Good Array
思路
最优的情况就是相差 \(1,2,3\dots\) 其差值序列为公差为 \(1\) 的等差数列,那么每一项也就是 \(1,2,4,7\dots\) 这样去构造,具体地,仔细观察得出第 \(i\) 项就是 \(1\) 加上差值数列的前 \(i-1\) 项和。
所以我们可以去二分找出 \(r-l\) 的差值里最大的前 \(x\) 项和(解方程也可以),那么就说明它最多可以组成 \(x+1\) 项了。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { i64 a, b; cin >> a >> b; i64 l = 1, r = 1e9, ans = 0; while (l <= r) { i64 mid = l + r >> 1; if ((1 + mid) * mid <= (b - a) * 2) { l = mid + 1; ans = mid; } else { r = mid - 1; } } cout << ans + 1 << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
D. Sakurako's Hobby
思路
手玩几个样例大概就能猜出结论,由于是排列的特性,所以最后能够互相到达的点就构成了一个环,那么 \(F(i)\) 就等于这个环里黑点的个数。
可以把这些环看成一个个联通块,用并查集维护黑点的个数即可。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; struct UFS { int sz; vector<int> rank, p, w; UFS(int n): sz(n) { rank.resize(n + 1); p.resize(n + 1); w.resize(n + 1); for (int i = 0; i <= sz; i++) { p[i] = i; rank[i] = 0; } } void link(int x, int y) { if (x == y) return; if (rank[x] > rank[y]) { p[y] = x; w[x] += w[y]; } else { p[x] = y; w[y] += w[x]; } if (rank[x] == rank[y]) rank[y]++; } int find(int x) { return x == p[x] ? x : (p[x] = find(p[x])); } void unin(int x, int y) { link(find(x), find(y)); } void compress() { for (int i = 0; i < sz; i++) find(i); } }; void solve() { int n; cin >> n; vector<int> a(n + 1); for (int i = 1; i <= n; i ++) { cin >> a[i]; } string s; cin >> s; s = " " + s; UFS ufs(n); for (int i = 1; i <= n; i ++) { ufs.w[i] = s[i] == '0'; } for (int i = 1; i <= n; i ++) { ufs.unin(i, a[i]); } for (int i = 1; i <= n; i ++) { cout << ufs.w[ufs.find(i)] << " \n"[i == n]; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
E. Alternating String
思路
分类讨论。
对于 \(|S|\) 为偶数的情况,是不能使用第一个操作的,也就是说它的奇偶上的字符是固定了的,那么这个时候只要分别统计一下奇偶字符上的最多的个数即可,然后用 \(|S|\) 减去它们。
对于 \(|S|\) 为奇数的情况,这个时候删掉中间某个字符,那么这个字符往后的位置,奇偶位置的字符就会互换,所以我们可以维护一个前缀奇偶字符的个数和后缀奇偶字符的个数,然后去枚举中间的字符,那么将这个位置删掉后,前面奇数位上的字符就和后面偶数位上的字符对应,偶数位上反之,这个时候再去统计两数位上最多的字符数即可,然后用 \(|S|\) 减去它们。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n; cin >> n; string s; cin >> s; if (n & 1) { int ans = 0; vector pre(2, vector<int>(30)); auto suf = pre; for (int i = 0; i < n; i ++) { suf[i & 1][s[i] - 'a'] ++; } for (int i = 0; i < n; i ++) { suf[i & 1][s[i] - 'a']--; int res = 0; for (int k = 0; k < 2; k ++) { int mx = 0; for (int j = 0; j < 26; j ++) { int t = pre[k][j] + suf[k ^ 1][j]; if (t > mx) { mx = t; } } res += mx; } pre[i & 1][s[i] - 'a']++; ans = max(ans, res); } ans = n - ans; cout << ans << '\n'; } else { int ans = 0; vector res(2, vector<int>(30)); for (int i = 0; i < n; i ++) { res[i & 1][s[i] - 'a'] ++; } for (int i = 0; i < 2; i ++) { auto mx = *max_element(res[i].begin(), res[i].end()); ans += mx; } ans = n - ans; cout << ans << '\n'; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
F. Sakurako's Box
思路
考虑 \(O(n^2)\) 做法,即令 \(S = \sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^n{a_i\times a_j}\),则 \(ans=\frac{S}{\frac{n\times (n-1)}{2}}\) 。
考虑优化。
展开 \(S\) 得:\(a_1\times (a_2+a_3+\dots+a_n)+a_2\times(a_3+\dots+a_n)\dots\),其实就是枚举 \(i\) 的时候,加上 \(a_i\times (\sum\limits_{j=1}^n a_j-\sum\limits_{j=1}^ia_j)\),而 \(\sum\limits_{j=1}^na_j\) 就是总和,可以预处理,\(\sum\limits_{j=1}^ia_j\) 是当前位置的前 \(i\) 项和,可以在枚举 \(i\) 的时候 \(O(1)\) 计算,那么最后按照上述公式计算即可。
此处用了封装好的取模类重载了部分运算符,实际运算中还是需要用逆元等计算。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; //------取模机------// 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; } // 快速幂 constexpr i64 mul(i64 a, i64 b, i64 p) { i64 res = a * b - i64(1.L * a * b / p) * p; res %= p; if (res < 0) { res += p; } return res; } // 取模乘 template<i64 P> struct MInt { i64 x; constexpr MInt() : x {0} {} constexpr MInt(i64 x) : x {norm(x % getMod())} {} static i64 Mod; constexpr static i64 getMod() { if (P > 0) { return P; } else { return Mod; } } constexpr static void setMod(i64 Mod_) { Mod = Mod_; }//只有P<=0, setMod才生效 constexpr i64 norm(i64 x) const { if (x < 0) { x += getMod(); } if (x >= getMod()) { x -= getMod(); } return x; } constexpr i64 val() const { return x; } constexpr MInt operator-() const { MInt res; res.x = norm(getMod() - x); return res; } constexpr MInt inv() const { return power(*this, getMod() - 2); } constexpr MInt &operator*=(MInt rhs) & { if (getMod() < (1ULL << 31)) { x = x * rhs.x % int(getMod()); } else { x = mul(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(); } friend constexpr bool operator<(MInt lhs, MInt rhs) { return lhs.val() < rhs.val(); } }; template<> i64 MInt<0>::Mod = 1000000007; //只有P<=0, Mod才生效 constexpr int P = 1000000007; //在这设置要用的模数 using Z = MInt<P>; //------取模机------// void solve() { int n; cin >> n; Z sum = 0; vector<Z> a(n + 1); for (int i = 1; i <= n; i ++) { cin >> a[i]; sum += a[i]; } Z res = 0, pre = 0; for (int i = 1; i < n; i ++) { pre += a[i]; res += a[i] * (sum - pre); } Z m = n; Z ans = res / (m * (m - 1) / 2); cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
G. Sakurako's Task
思路
要使得 \(mex_k\) 最大,那么每个数都应该尽量地往前靠,那么最后数组应该变成什么样才是最佳的呢?
考虑题目中操作的性质,当 \(a_i\) 和 \(a_j\) 互质时,那么 \(a_i\) 和 \(a_j\) 一定可以互相减去或相加直到变成两个相邻的数;当 \(a_i\) 和 \(a_j\) 不互质时,那么 \(a_i\) 和 \(a_j\) 只能通过减去和相加变成两个相隔 \(\gcd(a_i,a_j)\) 的数。
根据以上结论,我们可以计算出 \(\gcd(a_1,a_2,\dots,a_n)=g\),那么最终的序列一定就只能变成 \(0,g,2g\dots\) 可以证明,无法有更佳答案。
简单证明一下,如果存在更佳答案,那么这个更佳答案一定存在两数之间差距小于 \(g\) ,而 \(\gcd(a_1,a_2,\dots,a_n)\le \gcd(a_i,a_j)(1\le i,j\le n,i\ne j)\),由第二段结论可知,两两最小的差距就是 \(\gcd(a_i,a_j)\),与此相矛盾,证毕。
得到最终序列后,就是枚举找一下第 \(k\) 个在什么位置即可,注意 \(n=1\) 的时候需要特判一下。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n, k; cin >> n >> k; int g = 0; vector<int> a(n + 1); for (int i = 1; i <= n; i ++) { cin >> a[i]; g = gcd(a[i], g); } if (n == 1) { cout << (k <= a[1] ? k - 1 : k) << '\n'; return ; } for (int i = 1; i <= n; i ++) { a[i] = (i - 1) * g; } for (int i = 2; i <= n; i ++) { int x = a[i] - a[i - 1] - 1; if (k > x) { k -= x; } else { cout << a[i - 1] + k << '\n'; return ; } } cout << a[n] + k << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
H. Sakurako's Test
思路
题目要求最小中位数,那么每个数都要尽可能地减去 \(x\),则这个操作实际上就是 \(a_i \bmod x\),也就是求所有数模 \(x\) 之后的中位数。
对于 \(a_i \bmod x\),可以看成 \(a_i=k\times x+b\),那如果将原数组按值域划分区域 \([0,x),[x,2x)\dots\),那么 \(a_i\) 就属于第 \(k+1\) 个区域,假设当前我们要判断 \(m\) 能不能作为中位数,那就要看 \(b\) 是否小于等于 \(m\) 从而判断 \(a_i\) 是否产生贡献即可。由于所有数都模了 \(x\),所以 \(0\le m< x\),所以我们可以首先做个值域前缀和,然后对每个区域通过作差计算在 \([l,l+m]\) 之中的数有多少,最后将这些贡献统计后再与 \(\frac{n}{2}+1\) 作比较判断 \(m\) 能不能作为中位数。
题目要求最小中位数,那么判断 \(m\) 是否可以作为中位数可以利用以上结论,又知道 \(m\in[0,x)\),那么二分即可。
找出所有可能的 \(x\) 的最小中位数之后,记录一下,那么对于询问就可以 \(O(1)\) 得出了。
代码
#include <bits/stdc++.h> using namespace std; using i64 = long long; void solve() { int n, q; cin >> n >> q; vector<int> pre(n + 1); for (int i = 0; i < n; i ++) { int x; cin >> x; pre[x] ++; } for (int i = 1; i <= n; i ++) { pre[i] += pre[i - 1]; } auto check = [&](int x, int m)->bool{ int res = 0; for (int l = 0; l <= n; l += x) { int r = min(l + m, n); res += pre[r] - (l ? pre[l - 1] : 0); } return res >= n / 2 + 1; }; vector<int> ans(n + 1); for (int x = 1; x <= n; x ++) { int l = 0, r = x; while (l <= r) { int mid = l + r >> 1; if (check(x, mid)) { r = mid - 1, ans[x] = mid; } else { l = mid + 1; } } } for (int i = 1; i <= q; i ++) { int x; cin >> x; cout << ans[x] << " \n"[i == q]; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { solve(); } return 0; }
本文作者:Ke_scholar
本文链接:https://www.cnblogs.com/Kescholar/p/18399473
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步