Codeforces Round #853 (Div. 2) 题解
Codeforces Round #853 (Div. 2) 题解
ABCD
Codeforces Round #853 (Div. 2) | 萌新实况被吊打 | ABCD 题解
E. Serval and Music Game
分两种情况讨论:
- .
- .
对于第一种,,即查询 中 的倍数有几个。
显然 ,则当 时,显然有 。那么我们可以把 扔到 上,查询的时候我们遍历 的因子,找出所有 ,然后统计 。
这一步的时间复杂度是 。(一共有 种 ,遍历因子是 的)
对于第二种,设 ,则查询 ,则 时都成立。
换个角度考虑, 可以表示出 ,所有区间即为
对 在值域上做前缀和即可 查询这些区间。
看起来这里似乎有 个区间,但实际上当 的时候,就已经可以表示任何数了。所以只有 个区间。
若 ,我们暴力计算上面那些 个区间。
对于 ,显然 ,所以区间也只有 种。
所以对于任意一个 ,查询都是 的,一共有 个 ,时间复杂度 。
总时间复杂度 。
// Problem: E. Serval and Music Game
// URL: https://codeforces.com/contest/1789/problem/E
// Group: Codeforces - Codeforces Round #853 (Div. 2)
// Time: 2023-02-25 22:20
// Author: lingfunny
#include <bits/stdc++.h>
using LL = long long;
using uint = unsigned;
using namespace std;
constexpr int mod = 998244353;
// assume -mod <= x < 2mod
int normZ(int x) {
if (x < 0) x += mod;
if (x >= mod) x -= mod;
return x;
}
template <typename T> T qpow(T x, LL k) {
T res = 1;
for (; k; k >>= 1, x *= x)
if (k & 1) res *= x;
return res;
}
struct Z {
int x;
Z(int x = 0) : x(normZ(x)) {}
Z(LL x) : x(normZ(x % mod)) {}
int val() const { return x; }
Z operator-() const { return Z(normZ(mod - x)); }
Z inv() const {
assert(x != 0);
return qpow(*this, mod - 2);
}
Z &operator*=(const Z &rhs) {
x = (LL)x * rhs.x % mod;
return *this;
}
Z &operator+=(const Z &rhs) {
x = normZ(x + rhs.x);
return *this;
}
Z &operator-=(const Z &rhs) {
x = normZ(x - rhs.x);
return *this;
}
Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); }
friend Z operator*(const Z &lhs, const Z &rhs) {
Z res = lhs;
res *= rhs;
return res;
}
friend Z operator+(const Z &lhs, const Z &rhs) {
Z res = lhs;
res += rhs;
return res;
}
friend Z operator-(const Z &lhs, const Z &rhs) {
Z res = lhs;
res -= rhs;
return res;
}
friend Z operator/(const Z &lhs, const Z &rhs) {
Z res = lhs;
res /= rhs;
return res;
}
};
const int mxs = 1e7 + 10, mxn = 1e6 + 10;
int TestCase, n, a[mxn], s[mxs], R[mxs], cnt[mxs];
Z ans;
signed main() {
for (scanf("%d", &TestCase); TestCase--;) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", a + i), ++s[a[i]];
for (int i = 1; i <= a[n]; ++i) s[i] += s[i - 1];
for (int i = 0; i <= a[n]; ++i) {
cnt[i] = 0;R[i] = -1;
}
for (int i = 1; i <= n; ++i) ++cnt[gcd(a[i], a[n])];
for (int I = 1; I <= a[n]; ++I)
if (a[n] % I) {
int k = a[n] / I;
if (!~R[k]) {
R[k] = 0;
for (int i = 1; i * k <= a[n] && i < k; ++i) {
int r = min(i * k + i, a[n]), l = i * k;
R[k] += s[r] - s[l - 1];
}
if ((LL)k * k <= a[n]) R[k] += s[a[n]] - s[k * k - 1];
}
ans += (LL)R[k] * I % mod;
} else {
int sum = 0;
int k = a[n] / I;
for (int d = 1; d * d <= a[n]; ++d)
if (a[n] % d == 0) {
if (d % k == 0) sum += cnt[d];
int D = a[n] / d;
if (D != d && D % k == 0) sum += cnt[D];
}
ans += (LL)sum * I % mod;
}
printf("%d\n", ans.val()), ans = 0;
for (int i = 1; i <= a[n]; ++i) s[i] = 0;
}
return 0;
}
F. Serval and Brain Power
有一个限制条件是 。
我们对左边这个式子平衡规划。
对于 时,显然 包含 。
考虑 ,我们暴力枚举 的 种拆解方式,然后做 。dp 的时间复杂度是 的,这里的总时间复杂度是 的。
考虑 ,同理的,枚举 的 种拆解方式,然后做 。dp 的时间复杂度是 的,总时间复杂度是 的。
有一个好事是,显然我们的拆解方式是不满的。我们考虑这个过程的组合意义。我们把 个白球和 个黑球排成一列,求本质不同的方案数。
这里的第 和第 个黑球是序列的拆解方案数,而 可以放在任意位置,方案数为 。
则方案数为 ,还是很稳的。
对于 ,显然有 ,并且 出现了 次以上。如果把 划分为五段长度相等的部分,则 至少会完整地出现在某个段中。
于是我们对每个段, 枚举所有可能的子序列,并进行一次 的贪心匹配。时间复杂度 ,也是很稳的。
// Problem: F. Serval and Brain Power
// URL: https://codeforces.com/contest/1789/problem/F
// Group: Codeforces - Codeforces Round #853 (Div. 2)
// Time: 2023-02-25 22:20
// Author: lingfunny
#include <bits/stdc++.h>
using LL = long long;
using uint = unsigned;
using namespace std;
const int mxn = 85;
int n;
char s[mxn], t[mxn];
signed main() {
scanf("%s", s + 1);
n = strlen(s + 1);
auto solve2 = [&](char s1[], char s2[], int n, int m) {
static int f[mxn][mxn];
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= m; ++j) f[i][j] = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) f[i][j] = max({ f[i - 1][j], f[i][j - 1], f[i - 1][j - 1] + (s1[i] == s2[j]) });
return f[n][m] * 2;
};
auto solve3 = [&](char s1[], char s2[], char s3[], int n, int m, int q) {
static int f[mxn][mxn][mxn];
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= m; ++j)
for (int k = 0; k <= q; ++k) f[i][j][k] = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
for (int k = 1; k <= q; ++k)
f[i][j][k] = max({ f[i - 1][j][k], f[i][j - 1][k], f[i][j][k - 1], f[i - 1][j - 1][k - 1] + (s1[i] == s2[j] && s2[j] == s3[k]) });
return f[n][m][q] * 3;
};
auto match = [&](int m) {
int cnt = 0;
for (int i = 1; i <= n; ++i)
if (s[i] == t[cnt % m]) ++cnt;
return cnt >= m * 2 ? cnt / m * m : 0;
};
int ans = 0;
for (int i = 1; i < n; ++i) ans = max(ans, solve2(s, s + i, i, n - i));
for (int i = 1; i < n; ++i)
for (int j = 1; i + j < n; ++j) ans = max(ans, solve3(s, s + i, s + i + j, i, j, n - i - j));
for (int l = 1, r; l <= n; l = r + 1) {
r = min(l + (n + 4) / 5, n);
int len = r - l + 1, tot = 1 << len;
for (int S = 1; S < tot; ++S) {
int m = 0;
for (int i = 0; i < len; ++i)
if ((S >> i) & 1) t[m++] = s[l + i];
ans = max(ans, match(m));
}
}
printf("%d\n", ans);
return 0;
}
END
E 和 F 在复杂度分析上都很有意思!
E 的「查询 的倍数在 中的出现次数」也很值得思考。这里的做法(代码里)是 处理 的。实际上如果设函数 ,这个东西是个积性函数。可以用欧拉筛在 内算出来。此题可以做到准 。
本文来自博客园,作者:lingfunny,转载请注明原文链接:https://www.cnblogs.com/lingfunny/p/17156323.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效