Educational Codeforces Round 86 (Rated for Div. 2) 部分题解
Educational Codeforces Round 86 (Rated for Div. 2)
A. Road To Zero
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int t; cin >> t;
while (t--) {
ll x, y, a, b;
cin >> x >> y >> a >> b;
b = min(a + a, b);
cout << min(x, y) * b + (max(x, y) - min(x, y)) * a << "\n";
}
}
B. Binary Period
题意:给出一个只由 \(0,1\) 构成的子串 \(t\) ,要求你给出一个原串的构造,并且满足原串长度不大于两倍子串长度,同时原串的循环节最短。
分析:除了全零或者全一的情况,最短的循环节一定是 \(01\) 或者 \(10\) ,因此构造一个 \(10101010\cdots\) 的序列,长度为两倍子串长度。这个构造的正确性可以从最极端的反例:子串 \(000\cdots111\) 证明(前一半是 \(0\),后一半是 \(1\)),不难发现两倍长度必定够了。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int t; cin >> t;
while (t--) {
string s; cin >> s;
int zero = 0, one = 0;
for (auto i : s) {
if (i == '1') one++;
else zero++;
}
if (!zero || !one) cout << s << '\n';
else {
int n = s.length();
while (n--) cout << "10";
cout << '\n';
}
}
}
C. Yet Another Counting Problem
题意:询问 \([l,r]\) 中有几个数满足 \((x\pmod{a}\bmod{b})\neq(x\pmod{b}\bmod{a})\) 。
分析:由于 \(a,b\leq200\) ,因此直接枚举模 \(ab\) 的同余类(更准确地说,应该枚举 \(LCM(a,b)\)),求一个 \([0,a*b)\) 的完全剩余系前缀和,就能 \(O(1)\) 分段回答询问了。时间复杂度 \(O(t(ab+q))\) 。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll a, b, q;
vector<ll> pre;
ll calc(ll x) {
return x / (a * b) * pre[a * b] + pre[x % (a * b)];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int t; cin >> t;
while (t--) {
cin >> a >> b >> q;
pre.resize(a * b + 1, 0);
for (int i = 0; i < a * b; ++i)
pre[i + 1] = pre[i] + (i % a % b != i % b % a);
while (q--) {
ll l, r;
cin >> l >> r;
cout << calc(r + 1) - calc(l) << ' ';
}
cout << '\n';
}
}
D. Multiple Testcases
题意:给定一个大小为 \(n\) 的数组 \(m_n\) ,并且该数组中的最大元素不超过 \(k\) 。要求给出一种构造,将这个数组分配给多个集合,每个集合中的元素必须满足大小 \(\geq i\) 的元素数量不超过 \(c_i\) 。
分析:因为 \(c_i\) 是一个单调不增的数列,因此本题就是一个从大到小放的贪心,我们优先向一个集合里放入较大的元素。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int n, k;
cin >> n >> k;
vector<int> cnt(k + 1);
vector<int> c(k + 1);
for (int i = 0; i < n; ++i) {
int x; cin >> x;
cnt[x]++;
}
for (int i = 1; i <= k; ++i) cin >> c[i];
vector<vector<int> > res(1);
int pre = 0, ans = 0;
for (int i = k; i; --i) {
int now = 0;
if (i != k && c[i] == c[i + 1]) now = pre;
while (cnt[i]) {
if (now == ans) {
++ans;
res.emplace_back();
continue;
}
if (res[now].size() < c[i]) {
res[now].emplace_back(i);
--cnt[i];
}
else ++now;
}
pre = now;
}
cout << ans << '\n';
for (int i = 0; i < ans; ++i) {
cout << res[i].size();
for (auto j : res[i]) cout << ' ' << j;
cout << '\n';
}
}
E. Placing Rooks
题意:给定一个 \(n\times n\) 的棋盘,要求你在棋盘上放 \(n\) 个车(国际象棋),使得所有格子都能被车在一步内攻击到,询问正好存在 \(k\) 对车能够相互攻击(不能跨过棋子攻击)的棋子摆法有几种。
分析:首先,由于所有格子都需要被车在一步内攻击到,因此该棋盘每一行或者每一列都至少有一个车,由此我们能够立刻推出 \(F(n,0)=n!\) 。并且,由于车不能跨过棋子攻击,因此最多存在 \(k-1\) 对车能够互相攻击,即 \(F(n,k)=0(k\geq n)\) 。
然后,我们考虑存在 \(k(n>k>0)\) 对车能够相互攻击的情况,不妨假设该棋盘每一行至少有一个车(和每一列至少有一个车的情况是完全对称的,因此结果乘 \(2\) 即可)。我们将这 \(n\) 个车看作图上的点,在能够互相攻击的车之间连线,一共需要连 \(k\) 条线,因此相当于 \(n-k\) 个连通分量,问题转化为将 \(n\) 个元素划分为 \(n-k\) 个子集有几种方法,实际上就是在求第二类斯特林数 \(S^{n-k}_n\) 。然后,由于每一行都至少有一个车,因此只有 \(n-k\) 列中有车,相当于在总共 \(n\) 列的棋盘中选择 \(n-k\) 列,将答案乘上 \(C^{n-k}_{n}\) ;并且我们考虑到这 \(n-k\) 个子集是无序的,因此再乘上 \((n-k)!\) 。因此本题的答案为 \(2S^{n-k}_nC^{n-k}_n(n-k)!\pmod{998244353}\) 。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod = 998244353;
ll qpow(ll a, ll b, ll mod) {
ll ans = 1;
while (b) {
if (b & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
ll n, k, ans = 0;
cin >> n >> k;
if (k >= n) {
cout << 0;
return 0;
}
vector<ll> fac(n + 1, 1);
for (int i = 2; i <= n; ++i) fac[i] = fac[i - 1] * i % mod;
auto C = [&](ll n, ll m) {
return fac[n] * qpow(fac[m], mod - 2ll, mod) % mod * qpow(fac[n - m], mod - 2ll, mod) % mod;
};
for (int i = 0; i <= n - k; ++i) {
ans += ((n - k - i) % 2 == 0 ? 1ll : -1ll) * C(n - k, i) * qpow(i, n, mod) % mod;
ans %= mod;
}
ans = (ans + mod) % mod;
ans = (k ? 2ll : 1ll) * ans * C(n, n - k) % mod;
cout << ans;
}