VP Educational Codeforces Round 71 (Rated for Div. 2)
A. There Are Two Types Of Burgers
题意:两个面包加牛肉得到牛肉汉堡,两个面包加鸡肉得到鸡肉汉堡。两个汉堡有价格,有\(b\)个面包,\(p\)牛肉,\(f\)鸡肉。求最大利润。
优先卖贵的。
点击查看代码
void solve() {
int b, p, f, h, c;
std::cin >> b >> p >> f >> h >> c;
if (h < c) {
std::swap(p, f);
std::swap(h, c);
}
int v = std::min(b / 2, p);
int ans = v * h;
b -= v * 2;
v = std::min(b / 2, f);
ans += v * c;
std::cout << ans << "\n";
}
B. Square Filling
题意:两个01矩阵\(A, B\)。\(B\)一开始全零,你每次操作\(B\)的一个\(2\times 2\)的矩阵,把它们都变成\(1\)。求\(B\)变成\(A\)的操作。
把\(A\)里每个\(2\times 2\)的矩阵操作,看能不能从\(B\)变成\(A\)。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector a(n, std::vector<int>(m));
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
std::cin >> a[i][j];
}
}
std::vector b(n, std::vector<int>(m));
std::vector<std::pair<int, int>> ans;
for (int i = 0; i + 1 < n; ++ i) {
for (int j = 0; j +1 < m; ++ j) {
if (a[i][j] == 1 && a[i + 1][j] == 1 && a[i][j + 1] == 1 && a[i + 1][j + 1] == 1) {
ans.emplace_back(i, j);
b[i][j] = b[i + 1][j] = b[i][j + 1] = b[i + 1][j + 1] = 1;
}
}
}
for (int i = 0; i < n; ++ i) {
if (a[i] != b[i]) {
std::cout << -1 << "\n";
return;
}
}
std::cout << ans.size() << "\n";
for (auto & [x, y] : ans) {
std::cout << x + 1 << " " << y + 1 << "\n";
}
}
C. Gas Pipeline
题意:给你一个字符串,如果\(s_i = 1\)则\(c_i = c_{i + 1} = 1\)。要求每个\(c_{i}\)等于\(1\)的地方高度为\(2\),\(c_i = 0\)的高度可以为1可以为2。每个位置价值为高度乘\(b\)。两个位置高度相同则价值为\(a\),否则为\(2a\)。求最小价值。
\(dp\)。\(f[i][0/1]\)代表\(i\)高度为1或2的最小代价,讨论前面位置对当前状态的贡献。
点击查看代码
void solve() {
i64 n, a, b;
std::cin >> n >> a >> b;
std::string s;
std::cin >> s;
std::vector<int> c(n + 1);
for (int i = 0; i < n; ++ i) {
if (s[i] == '1') {
c[i] = c[i + 1] = 1;
}
}
const i64 inf = 1e18;
std::array<i64, 2> f{inf, inf};
f[0] = b;
for (int i = 1; i <= n; ++ i) {
std::array<i64, 2> g{inf, inf};
g[1] = std::min(f[0] + 2 * a, f[1] + a) + 2 * b;
if (c[i] == 0) {
g[0] = std::min(f[1] + 2 * a, f[0] + a) + b;
}
f = g;
}
std::cout << f[0] << "\n";
}
D. Number Of Permutations
题意:\(n\)个二元组,对于一个排列,有\(b_i = a_{p_i}\)。求有多少排列使得这个二元组的两个关键字拿出来都不是递增的。
容斥。
总方案数 - 第一关键字递增的方案数 - 第二关键字递增的方案数 + 两个都递增的方案数。
对于每个关键字,可以按这个关键字排序,你们每一段相等的数可以进行一个排序,使得依旧递增。
两个都递增的方案数可以先按第一关键字排序,然后判断两个关键字都相等的一段。注意如果有按第一关键字递增导致第二关键字不能递增的情况方案数就是零。
点击查看代码
const int mod = 998244353;
int power(int a, int b) {
int res = 1;
for (;b;b >>= 1, a = 1LL * a * a % mod) {
if (b & 1) {
res = 1LL * res * a % mod;
}
}
return res;
}
int inv(int n) {
return power(n, mod - 2);
}
void solve() {
int n;
std::cin >> n;
std::vector<std::pair<int, int>> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i].first >> a[i].second;
}
std::vector<int> fact(n + 1), infact(n + 1);
fact[0] = 1;
for (int i = 1; i <= n; ++ i) {
fact[i] = (i64)fact[i - 1] * i % mod;
}
infact[n] = inv(fact[n]);
for (int i = n; i >= 1; -- i) {
infact[i - 1] = (i64)infact[i] * i % mod;
}
int ans1 = 1, ans2 = 1, ans3 = 1;
std::ranges::sort(a);
for (int i = 0; i < n; ++ i) {
int j = i;
while (j + 1 < n && a[i].first == a[j + 1].first) {
++ j;
}
ans1 = (i64)ans1 * fact[j - i + 1] % mod;
i = j;
}
std::ranges::sort(a, [&](std::pair<int, int> & a, std::pair<int, int> & b) {
return a.second < b.second;
});
for (int i = 0; i < n; ++ i) {
int j = i;
while (j + 1 < n && a[i].second == a[j + 1].second) {
++ j;
}
ans2 = (i64)ans2 * fact[j - i + 1] % mod;
i = j;
}
std::ranges::sort(a);
for (int i = 0; i < n; ++ i) {
int j = i;
while (j + 1 < n && a[j + 1] == a[i]) {
++ j;
}
ans3 = (i64)ans3 * fact[j - i + 1] % mod;
i = j;
}
for (int i = 0; i + 1 < n; ++ i) {
if (a[i].second > a[i + 1].second) {
ans3 = 0;
break;
}
}
int ans = fact[n];
ans = (ans - ans1 + mod) % mod;
ans = (ans - ans2 + mod) % mod;
ans = (ans + ans3) % mod;
std::cout << ans << "\n";
}
E. XOR Guessing
题意:交互题。一个范围在\([1, 2^14 - 1]\)的数,一开始你不知道。你最多询问两次,每次给出100个不同的数。每次随机选一个你给出的数进行异或把结果返回。求这个数。
询问一组二进制下奇数位都是0的全不同的数和一组偶数位都是0的全不同的数。那么就可以根据两个结果凑出这个数。
点击查看代码
int ask(std::vector<int> & a) {
std::cout << "?";
for (int i = 0; i < 100; ++ i) {
std::cout << " " << a[i];
}
std::cout << std::endl;
int res;
std::cin >> res;
return res;
}
void solve() {
std::vector<int> a(100);
std::ranges::iota(a, 1);
std::vector<int> q(100);
for (int i = 0; i < 100; ++ i) {
for (int j = 1; j < 14; j += 2) {
if ((a[i] >> j / 2) & 1) {
q[i] += 1 << j;
}
}
}
int x = ask(q);
std::ranges::fill(q, 0);
for (int i = 0; i < 100; ++ i) {
for (int j = 0; j < 14; j += 2) {
if ((a[i] >> j / 2) & 1) {
q[i] += 1 << j;
}
}
}
int y = ask(q);
int ans = 0;
for (int i = 0; i < 14; ++ i) {
if (i & 1) {
if (y >> i & 1) {
ans += 1 << i;
}
} else {
if (x >> i & 1) {
ans += 1 << i;
}
}
}
std::cout << "! " << ans << std::endl;
}
F. Remainder Problem
题意:一个长度为\(50000\)的序列。有两个操作:
- 给\(a_x\)加上\(y\)。
- 询问所有取模\(x\)为\(y\)的位置的值的和。
根号分治。
如果\(x > \sqrt(50000)\),直接暴力。
否则我们存一个\(sum1[i][j]\)表示取模\(i\)为\(j\)的位置的和。就可以了。
点击查看代码
void solve() {
int q;
std::cin >> q;
const int N = 500000, M = std::sqrt(N) + 1;
std::vector<i64> sum(N + 1);
std::vector sum1(M + 1, std::vector<i64>(M));
while (q -- ) {
int op, x, y;
std::cin >> op >> x >> y;
if (op == 1) {
sum[x] += y;
for (int i = 1; i <= M; ++ i) {
sum1[i][x % i] += y;
}
} else {
i64 ans = 0;
if (x > M) {
for (int i = y; i <= N; i += x) {
ans += sum[i];
}
} else {
ans = sum1[x][y];
}
std::cout << ans << "\n";
}
}
}