Teza Round 1 (Codeforces Round 1015, Div. 1 + Div. 2)
A. Max and Mod
题意:构造一个排列,使得\(\max(p_i, p_{i-1}) = i - 1\)。
发现\(n\)为奇数时输出\(n, 1, 2, ... , n - 1\)就行。
如果为偶数,无解。(猜的)
点击查看代码
void solve() {
int n;
std::cin >> n;
if (n % 2 == 0) {
std::cout << -1 << "\n";
} else {
std::cout << n << " ";
for (int i = 1; i < n; ++ i) {
std::cout << i << " \n"[i == n - 1];
}
}
}
B. MIN = GCD
题意:给你一个数组,重新排列后使得数组可以分成两部分,前一部分的最小值和后一部分的\(gcd\)相等。
不管最小值在哪一个部分,这一部分的值都小于等于它。
然后\(gcd\)肯定小于等于其中的最小值,所以我们应该放至少一个最小值在前面。然后后面都放最小值的倍数。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::ranges::sort(a);
i64 d = 0;
for (int i = 1; i < n; ++ i) {
if (a[i] % a[0] == 0) {
d = std::gcd(d, a[i]);
}
}
if (d == a[0]) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
C. You Soared Afar With Grace
题意:给你两个排列,你每次交换\(a_i, a_j\)和\(b_i, b_j\)。求能不能使得\(a\)和\(b\)是反着的。
单独考虑\(a\)或\(b\)都无法解决问题。不妨把两个数组看作一个数组\(c\),\(c_i = \{a_i, b_i\}\)。那么显然我们需要让\(\{a_i, b_i\}\)和\(\{b_i, a_i\}\)出现在对称的位置。
判断一些无解的情况后模拟就行。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n), b(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
-- a[i];
}
for (int i = 0; i < n; ++ i) {
std::cin >> b[i];
-- b[i];
}
int cnt = 0;
for (int i = 0; i < n; ++ i) {
cnt += a[i] == b[i];
}
if (cnt > n % 2) {
std::cout << -1 << "\n";
return;
}
std::vector<std::pair<int, int>> ans;
for (int i = 0; i < n; ++ i) {
if (a[i] == b[i]) {
std::swap(a[i], a[n / 2]);
std::swap(b[i], b[n / 2]);
if (i != n / 2) {
ans.emplace_back(i, n / 2);
}
break;
}
}
std::map<std::pair<int, int>, int> p;
for (int i = 0; i < n; ++ i) {
p[{a[i], b[i]}] = i;
}
for (int i = 0; i < n / 2; ++ i) {
if (!p.count({b[i], a[i]})) {
std::cout << -1 << "\n";
return;
}
int j = p[{b[i], a[i]}];
if (j == n - 1 - i) {
continue;
}
std::swap(p[{b[i], a[i]}], p[{a[n - 1 - i], b[n - 1 - i]}]);
std::swap(a[n - 1 - i], a[j]);
std::swap(b[n - 1 - i], b[j]);
ans.emplace_back(n - 1 - i, j);
}
for (int i = 0; i < n; ++ i) {
if (a[i] != b[n - 1 - i]) {
std::cout << -1 << "\n";
return;
}
}
std::cout << ans.size() << "\n";
for (auto & [i, j] : ans) {
std::cout << i + 1 << " " << j + 1 << "\n";
}
}
D. Arcology On Permafrost
题意:如果给你一个数组\(a\),你需要让它的\(mex\)最小,每次可以删除它的一个长度为\(k\)的子数组,最大操作\(m\)次。现在给你\(n, m, k\),要求你构造一个数组,使得操作后\(mex\)是所有长度为\(n\)的数组里最大的。
如果答案是\(d\),数组一定是\(0, 1, 2 ... d, 0, 1, 2.. d, ..\)这样。
怎么找到这个\(d\)?发现会删除\(m\)次,那么\([1, d]\)的每个数至少出现\(m + 1\)次。于是\(d\)最大是\(\lfloor \frac{n}{m+1} \rfloor\)。
然后发现还有\(k\)没考虑,也就是说\(0, 1, 2 .. d, .. 0\)每个相同数之间至少间隔\(\max(d, k)\)。那么就这样构造能得到最大。
点击查看代码
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
std::vector<int> ans(n);
int d = n / (m + 1);
for (int i = 0; i < d; ++ i) {
for (int j = i; j < n; j += std::max(k, d)) {
ans[j] = i;
}
}
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] << " \n"[i == n - 1];
}
}
E. Blossom
题意:一个\([0, n - 1]\)的排列,有些数是\(-1\)代表没有给出。求所有排列的所有子区间的\(mex\)之和。
如果我们枚举\(mex\),并且求所有大于等于当前\(mex\)的区间数,那么本来一个\(mex\)为\(k\)的区间贡献是\(k\),我们把它拆成了\(k\)个\(1\)的贡献,那么我们只需要枚举\(mex\),那么所有大于等于当前\(mex\)的区间数都会有\(1\)的贡献。
考虑\(mex\)为\(k\),我们需要考虑\(-1\)带来的影响,那么设当前区间有\(c1\)个\(-1\),需要填上\(c2\)个\(-1\)使得\(mex\)至少为\(k\),然后总共有\(c3\)个\(-1\),那么这个区间的贡献为\(C(c1, c2) \times c2! \times (c3 - c2)!\),意味\(c1\)个里选出\(c2\)个填充\(mex\),然后这\(c2\)个位置怎么填无所谓,只需要让\(mex = k\)就行,所以有\(c2!\)中方案,同时剩下\(c3 - c2\)个\(-1\)也是随便排,所以有\((c3 - c2)!\)的方案。然后化简(其实化不化无所谓):\(C(c1, c2) \times c2! \times (c3 - c2)! = \frac{c1!}{c2!(c1 - c2)!} \times c2! \times (c3 - c2)! = \frac{c1!}{(c1-c2)!}\times (c3-c2)!\),这样只是可以不用算组合数。
然后计算\(cnt_{-1} = c1\)的区间有多少个,这个可以预处理算出来。那么我们可以枚举\(mex\)同时枚举\(c1\),但这样会算不对,因为我们还需要满足,如果\(mex = k\),那么\([0, mex - 1]\)的数要么没出现,要么在当前区间里,显然会多算。那么考虑删掉这些区间的贡献,假设之前已经得到满足包含\([0, k - 1]\)的区间的左边界为\(l\),右边界为\(r\),那么之前已经把不包含\([l, r]\)的区间都删掉了,考虑\(pos_{k}\)的位置,如果\(pos_k < l\),那么需要把包含了\([l, r]\)但不包含\([pos_k, r]\)的区间删掉,\(pos_k > r\)同样处理。这样枚举区间一开始都被加入一次,每个\(k\)删去的区间都是不一样的,那么每个区间只会被删除一次,复杂度得到了保证。
代码省略取模类。
点击查看代码
struct Comb {
int n;
std::vector<Z> _fac;
std::vector<Z> _invfac;
std::vector<Z> _inv;
Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}
Comb(int n) : Comb() {
init(n);
}
void init(int m) {
if (m <= n) return;
_fac.resize(m + 1);
_invfac.resize(m + 1);
_inv.resize(m + 1);
for (int i = n + 1; i <= m; i++) {
_fac[i] = _fac[i - 1] * i;
}
_invfac[m] = _fac[m].inv();
for (int i = m; i > n; i--) {
_invfac[i - 1] = _invfac[i] * i;
_inv[i] = _invfac[i] * _fac[i - 1];
}
n = m;
}
Z fac(int m) {
if (m > n) init(2 * m);
return _fac[m];
}
Z invfac(int m) {
if (m > n) init(2 * m);
return _invfac[m];
}
Z inv(int m) {
if (m > n) init(2 * m);
return _inv[m];
}
Z binom(int n, int m) {
if (n < m || m < 0) return 0;
return fac(n) * invfac(m) * invfac(n - m);
}
} comb;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
std::vector<int> pos(n), sum(n + 1);
for (int i = 1; i <= n; ++ i) {
sum[i] = sum[i - 1];
if (a[i] == -1) {
++ sum[i];
} else {
pos[a[i]] = i;
}
}
std::vector<int> tot(n + 1);
for (int i = 1; i <= n; ++ i) {
for (int j = i; j <= n; ++ j) {
++ tot[sum[j] - sum[i - 1]];
}
}
Z ans = 0;
int cnt = 0, l = -1, r = -1;
for (int x = 0; x < n; ++ x) {
if (pos[x] != 0) {
if (l == -1) {
for (int i = 1; i < pos[x]; ++ i) {
for (int j = i; j < pos[x]; ++ j) {
-- tot[sum[j] - sum[i - 1]];
}
}
for (int i = pos[x] + 1; i <= n; ++ i) {
for (int j = i; j <= n; ++ j) {
-- tot[sum[j] - sum[i - 1]];
}
}
l = r = pos[x];
} else if (pos[x] < l) {
for (int i = pos[x] + 1; i <= l; ++ i) {
for (int j = r; j <= n; ++ j) {
-- tot[sum[j] - sum[i - 1]];
}
}
l = pos[x];
} else if (pos[x] > r) {
for (int i = 1; i <= l; ++ i) {
for (int j = r; j < pos[x]; ++ j) {
-- tot[sum[j] - sum[i - 1]];
}
}
r = pos[x];
}
} else {
++ cnt;
}
for (int k = cnt; k <= n; ++ k) {
ans += tot[k] * comb.fac(k) * comb.invfac(k - cnt) * comb.fac(sum[n] - cnt);
}
}
std::cout << ans << "\n";
}

浙公网安备 33010602011771号