Educational Codeforces Round 177 (Rated for Div. 2)
A. Cloudberry Jam
卡累利阿森林中最珍贵的浆果是云莓。为了用云莓制成果酱,我们需要取等量的云莓和糖,然后烹煮。也就是说,如果你有 \(2\) 公斤的云莓,你需要 \(2\) 公斤的糖。然而,使用 \(2\) 公斤云莓和 \(2\) 公斤糖制作出的果酱,并不会得到 \(4\) 公斤的果酱,而仅仅只有 3 公斤,因为在烹煮过程中会有部分果酱蒸发。具体来说,在标准烹煮过程中,果酱会蒸发掉四分之一(25%)。为了制作 \(n\) 个每个重 \(3\) 公斤的果酱罐头,需要多少公斤的云莓?
\(n \leq 10^8\)
每两公斤云莓生产三公斤果酱,答案就是 \(n \times 2\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef pair<int, pair<int, int> > piii;
typedef long long ll;
const int N = 2000086, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
printf("%d\n", n << 1);
}
return 0;
}
B. Large Array and Segments
有一个数组 \(a\),它由 \(n\) 个正整数组成,同时给定一个正整数 \(k\)。根据下列规则,从数组 \(a\) 构造出数组 \(b\):
- 数组 \(b\) 包含 \(n\cdot k\) 个数;
- 数组 \(b\) 的前 \(n\) 个数与数组 \(a\) 中的数相同,也就是 \(b_i=a_i\) 对于所有 \(i\le n\) 成立;
- 对于任意 \(i>n\),有 \(b_i=b_{i-n}\)。
例如,如果 \(a=[2,3,1,4]\) 且 \(k=3\),那么 \(b=[2,3,1,4,2,3,1,4,2,3,1,4]\)。现给定一个数 \(x\),要求统计这样的下标 \(l\) 的数量(\(1\le l\le n\cdot k\)),使得存在下标 \(r\ge l\),使得数组 \(b\) 在区间 \([l,r]\) 上的元素和至少为 \(x\)(换句话说,满足 \(b_l+b_{l+1}+\cdots+b_r\ge x\))。
\(n \leq 10^5, k \leq 10^5, x \leq 10^{18}\)
显然所有满足条件的 \(l\) 构成一个前缀,从左到右枚举周期,如果当前周期所有位置都满足条件,则直接加 \(n\),否则枚举当前周期的每一个位置判断是否满足条件,这个周期后面的所有周期都不存在满足条件的位置。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef pair<int, pair<int, int> > piii;
typedef long long ll;
const int N = 2000086, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
ll x;
void solve() {
ll sum = 0, res = 0;
for (int i = 1; i < n + 1; i++) sum += w[i];
ll r = sum * m;
for (int i = 1; i <= m; i++) {
if (r - sum + w[n] >= x) res += n;
else {
for (int j = 1; j <= n; j++) {
res += r >= x;
r -= w[j];
}
break;
}
r -= sum;
}
printf("%lld\n", res);
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n >> m >> x;
for (int i = 1; i < n + 1; i++) scanf("%d", w + i);
solve();
}
return 0;
}
C. Disappearing Permutation
给定一个从 \(1\) 到 \(n\) 的整数排列,这是一种大小为 \(n\) 的数组,其中每个整数从 \(1\) 到 \(n\) 恰好出现一次。现在给定一个排列 \(p\),它包含从 \(1\) 到 \(n\) 的整数。你需要处理 \(n\) 个查询。
在第 \(i\) 个查询中,你将 \(p_{d_i}\) 替换为 \(0\)。每个元素恰好被替换为 \(0\) 一次。查询中所做的更改是累积的,也就是说,在第 \(i\) 个查询之后,所有的整数 \(p_{d_1}, p_{d_2}, \ldots, p_{d_i}\) 都变为零。
每次查询之后,你需要找出修复数组所需的最小操作次数;换句话说,将当前数组转换为任意从 \(1\) 到 \(n\) 的排列(可能转换为原排列 \(p\),也可能转换为其他排列)。
你可以执行的修复数组的操作是:
选择一个整数 \(i\)(\(1\le i\le n\)),将数组的第 \(i\) 个元素替换为 \(i\)。
注意,每个查询的答案都是独立计算的,也就是说,你实际上并不执行任何操作,只需要计算出最小的操作次数即可。
\(n \leq 10^5\)
对每个位置 \(i\) 向 \(p_i\) 连一条有向边,这样形成的图会分成若干不相交的环。某个点 \(i\) 如果被替换成了 \(0\) 或者其他数,那么数组就会缺失 \(p_i\),\(i\) 的出边指向的点 \(p_i\) 就必须被操作。对于每一个环,如果环中存在至少一个点被替换成了 \(0\),那么环上所有点都需要进行操作。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef pair<int, pair<int, int> > piii;
typedef long long ll;
const int N = 2000086, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N];
int d[N];
bool st[N];
void dfs(int r) {
res++;
st[r] = 1;
if (!st[w[r]]) dfs(w[r]);
}
void solve() {
memset(st, 0, sizeof(bool) * (n + 10));
res = 0;
for (int i = 1; i < n + 1; i++) {
if (!st[d[i]]) dfs(d[i]);
printf("%lld ", res);
}
puts("");
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i < n + 1; i++) scanf("%d", w + i);
for (int i = 1; i < n + 1; i++) scanf("%d", d + i);
solve();
}
return 0;
}
D. Even String
你想构造一个由小写拉丁字母组成的字符串 \(s\),使得满足以下条件:
对于所有满足 \(s_i=s_j\) 的索引对 \(i\) 和 \(j\),它们之间的差值是偶数,即 \(|i-j|\mod2=0\)。
构造任意字符串太简单了,所以你会得到一个包含 26 个数字的数组 \(c\) —— 表示字符串 \(s\) 中每个字母要求出现的次数。也就是说,对于每个 \(i\in[1,26]\),拉丁字母表中的第 \(i\) 个字母应恰好出现 \(c_i\) 次。
你的任务是计算满足所有这些条件的不同字符串 \(s\) 的数量。由于答案可能非常大,请将结果对 \(998244353\) 取模。
\(c \leq 5 \times 10^5\)
题目的限制等价于对于每一个字符,要么所有都放到奇数位置,要么所有都放到偶数位置。考虑对每一个字符放置位置的奇偶性分配好后,令 \(N_{odd}\) 为奇数位置数量,\(N_{even}\) 为偶数位置数量,\(S\) 为选择放在奇数位置的字符的集合,不同的字符串个数有 \(\frac{N_{\mathrm{odd}}!}{\prod_{i\in S} c_i!} \times \frac{N_{\mathrm{even}}!}{\prod_{i\notin S} c_i!} = \frac{N_{\mathrm{odd}}! \cdot N_{\mathrm{even}}!}{\prod_{i=1}^{26} c_i!}\) 个。合法的奇偶位置分配方案数可以通过 dp 计算。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef pair<int, pair<int, int> > piii;
typedef long long ll;
const int N = 2000086, MOD = 998244353, INF = 0x3f3f3f3f;
ll res;
int n, m, cnt, w[N], v1, v2;
inline ll qmi(ll a, ll b, ll c) { ll res = 1; while (b) { if (b & 1) res = res * a % c; a = a * a % c; b >>= 1; } return res; }
ll fac[N * 8], inv[N * 8];
void init() {
fac[0] = 1;
for (int i = 1; i < N * 8; i++) fac[i] = fac[i - 1] * i % MOD;
inv[N * 8 - 1] = qmi(fac[N * 8 - 1], MOD - 2, MOD);
for (int i = N * 8 - 2; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % MOD;
}
void solve() {
res = 0;
m = 0;
for (int i = 1; i < n + 1; i++) m += w[i];
v1 = (m + 1) >> 1, v2 = m >> 1;
ll sum = fac[v1] * fac[v2] % MOD;
for (int i = 1; i < n + 1; i++) sum = sum * inv[w[i]] % MOD;
vector<ll> f(v1 + 10, 0);
f[0] = 1;
for (int i = 1; i <= 26; i++)
if (w[i])
for (int j = v1; j >= w[i]; j--)
f[j] = (f[j - w[i]] + f[j]) % MOD;
printf("%lld\n", f[v1] * sum % MOD);
}
int main() {
init();
int T;
cin >> T;
while (T--) {
n = 26;
for (int i = 1; i < n + 1; i++) scanf("%d", w + i);
solve();
}
return 0;
}