VP Educational Codeforces Round 8
A. Tennis Tournament
按题意模拟。
点击查看代码
void solve() {
int n, b, p;
std::cin >> n >> b >> p;
int ans1 = 0, ans2 = n * p;
while (n > 1) {
int k = 1;
while (k * 2 <= n) {
k *= 2;
}
ans1 += b * k + k / 2;
n -= k / 2;
}
std::cout << ans1 << " " << ans2 << "\n";
}
B. New Skateboard
题意:给你一个数字字符串,求这个字符串中有多少子串能被\(4\)整除。
末尾两位能被\(4\)整除,则这个数能被\(4\)整除。假设这个数为\(d_t \times 10^{t-1} + d_{t-1} \times 10^{t-2} + ... + d_2 \times 10^1 + d_1 \times 10^0\),那么就等于\((d_t \times 10^{t-3} + d_{t-1} \times 10^{t-4} + ... ) \times 100 + d_2 \times 10^1 + d_1 \times 10^0\),那么因为\(4 | 100\),则\(4 | (d_t \times 10^{t-3} + d_{t-1} \times 10^{t-4} + ... ) \times 100\),同理因为\(4 | d_2 \times 10^1 + d_1 \times 10^0\) 所以得到\(4 | (d_t \times 10^{t-3} + d_{t-1} \times 10^{t-4} + ... ) \times 100 + d_2 \times 10^1 + d_1 \times 10^0\)。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
int n = s.size();
i64 ans = 0;
for (int i = 0; i < n; ++ i) {
if ((s[i] - '0') % 4 == 0) {
++ ans;
}
if (i && ((s[i - 1] - '0') * 10 + s[i] - '0') % 4 == 0) {
ans += i;
}
}
std::cout << ans << "\n";
}
C. Bear and String Distance
题意:两个长度相同的字符的值定义为对应位置的字符之间相差字符的个数。现在给你一个字符串\(s\),要求构造一个和\(s\)的值为\(k\)的字符串。
因为可以取相等的字符,其位置上贡献是\(0\),那么我们可以贪心让前面的取尽量大的差距。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
for (auto & c : s) {
int x = 'z' - c, y = c - 'a';
if (x >= y && k >= x) {
c += x;
k -= x;
} else if (y >= x && k >= y) {
c -= y;
k -= y;
} else {
if (x >= k) {
c += k;
k = 0;
} else {
c -= k;
k = 0;
}
}
}
if (k > 0) {
std::cout << -1 << "\n";
return;
}
std::cout << s << "\n";
}
D. Magic Numbers
题意:求\([a, b]\)之间有多少个数字满足,\(d\)仅出现在所有偶数位置,且这个数字能被\(m\)整除。
考虑数位\(dp\),\(f[u][x][st]\)表示到了第\(u\)个位置模\(m\)为\(x\)且当前位置是奇数还是偶数。然后就是按照数位\(dp\)经典套路求\([0, b] - [0, a - 1]\)的值就行了。
点击查看代码
const int mod = 1e9 + 7;
void solve() {
int m, d;
std::cin >> m >> d;
std::string a, b;
std::cin >> a >> b;
auto work = [&](std::string & s) -> int {
int n = s.size();
std::vector f(n, std::vector(m, std::array<int, 2>{-1, -1}));
auto dfs = [&](auto self, int u, int x, bool st, bool zero, bool limit) -> int {
if (u == n) {
return x == 0;
}
if (!limit && !zero && f[u][x][st] != -1) {
return f[u][x][st];
}
int up = limit ? s[u] - '0' : 9;
int res = 0;
if (!zero && st) {
if (up >= d) {
res = self(self, u + 1, (x * 10 + d) % m, false, false, limit && d == up);
}
} else {
for (int i = 0; i <= up; ++ i) {
if (!(i == 0 && zero) && i == d) {
continue;
}
res = (res + self(self, u + 1, (x * 10 + i) % m, !(i == 0 && zero), i == 0 && zero, limit && i == up)) % mod;
}
}
if (!limit && !zero) {
f[u][x][st] = res;
}
return res;
};
return dfs(dfs, 0, 0, false, true, true);
};
int ans = 0;
if (a == "0") {
ans = work(b);
} else {
std::reverse(a.begin(), a.end());
for (int i = 0; i < a.size(); ++ i) {
if (a[i] == '0') {
a[i] = '9';
} else {
-- a[i];
break;
}
}
if (a.back() == '0') {
a.pop_back();
}
std::reverse(a.begin(), a.end());
ans = (work(b) - work(a) + mod) % mod;
}
std::cout << ans << "\n";
}
E. Zbazi in Zeydabad
题意:定义一个\(z\)形为一个正方形矩阵内反对角线以及第一行和最后一行全是\(z\)。求给出的字符矩阵中有多少\(z\)形。
预处理\(l[i][j], r[i][j], ld[i][j]\)表示\(i, j\)往左往右和往左下有多少连续的\(z\)。
考虑枚举\(z\)形的右上角\(i, j\),那么就是要求有多少和它一个对角线上并且在它下面且往右边可以延伸到大于等于\(j\)的位置。发现直接枚举复杂度较高,考虑优化枚举顺序,按列从右往左枚举,然后把同一条对角线上所有满足条件的点标为1,那么对于\(i, j\)就是求对角线上\([i, r]\)中有多少1,其中\(r = i + \min(l[i][j], ld[i][j]) - 1\)。给每一条对角线都用树状数组维护。
点击查看代码
template <class T>
struct Fenwick {
int n;
std::vector<T> tr;
Fenwick(int _n) {
init(_n);
}
void init(int _n) {
n = _n;
tr.assign(_n + 1, T{});
}
void add(int x, const T &v) {
for (int i = x; i <= n; i += i & -i) {
tr[i] = tr[i] + v;
}
}
T query(int x) {
T res{};
for (int i = x; i; i -= i & -i) {
res = res + tr[i];
}
return res;
}
T sum(int l, int r) {
return query(r) - query(l - 1);
}
};
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::string> s(n);
for (int i = 0; i < n; ++ i) {
std::cin >> s[i];
}
std::vector l(n, std::vector<int>(m));
std::vector r(n, std::vector<int>(m));
std::vector ld(n, std::vector<int>(m));
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if (s[i][j] == 'z') {
if (j == 0) {
l[i][j] = 1;
} else {
l[i][j] = l[i][j - 1] + 1;
}
}
}
for (int j = m - 1; j >= 0; -- j) {
if (s[i][j] == 'z') {
if (j == m - 1) {
r[i][j] = 1;
} else {
r[i][j] = r[i][j + 1] + 1;
}
}
}
}
for (int i = n - 1; i >= 0; -- i) {
for (int j = 0; j < m; ++ j) {
if (s[i][j] == 'z') {
if (i == n - 1 || j == 0) {
ld[i][j] = 1;
} else {
ld[i][j] = ld[i + 1][j - 1] + 1;
}
}
}
}
std::vector<std::vector<std::pair<int, int>>> a(m);
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if (s[i][j] == 'z') {
a[j + r[i][j] - 1].push_back({i, j});
}
}
}
std::vector<Fenwick<int>> tr(n + m, Fenwick<int>(n + 1));
i64 ans = 0;
for (int j = m - 1; j >= 0; -- j) {
for (auto & [x, y] : a[j]) {
tr[x + y].add(x + 1, 1);
}
for (int i = 0; i < n; ++ i) {
if (s[i][j] == 'z') {
int len = std::min(l[i][j], ld[i][j]);
int l = i, r = i + len - 1;
ans += tr[i + j].sum(l + 1, r + 1);
}
}
}
std::cout << ans << "\n";
}
F. Bear and Fair Set
题意:从\([1, b]\)里选\(n\)个数,其中\(n\)是五的倍数。满足模\(5\)等于\(0, 1, 2, 3, 4\)的数各有\(\frac{n}{5}\)个。有\(q\)个限制,第\(i\)个限制表示\([1, a]\)里需要恰好选\(k\)个。求有没有合法方案。
先判断限制有没有冲突,先把\((b, n)\)加入表示\([1, b]\)里要选\(n\)个。然后排序后判断是不是\(a_{i+1} - a_i \geq k_{i+1} - k_i\)且\(k_i \leq k_{i+1}\)。
合法的话,我们可以考虑用网络流求解,建立五个点,分别表示模\(5\)不同的值,每个点向汇点连\(\frac{n}{5}\)的流量,然后每个数对自己模\(5\)后对应的点连容量为1的边。最后遍历每个限制,源点向每个\((a_i, k_i)\)连\(k_i - k_{i-1}\)的流量,然后第\(i\)个限制向\([a_{i-1} + 1, a_i]\)每个点连容量为1的边。
点击查看代码
constexpr int inf = 1E9;
template<class T>
struct MaxFlow {
struct _Edge {
int to;
T cap;
_Edge(int to, T cap) : to(to), cap(cap) {}
};
int n;
std::vector<_Edge> e;
std::vector<std::vector<int>> g;
std::vector<int> cur, h;
MaxFlow() {}
MaxFlow(int n) {
init(n);
}
void init(int n) {
this->n = n;
e.clear();
g.assign(n, {});
cur.resize(n);
h.resize(n);
}
bool bfs(int s, int t) {
h.assign(n, -1);
std::queue<int> que;
h[s] = 0;
que.push(s);
while (!que.empty()) {
const int u = que.front();
que.pop();
for (int i : g[u]) {
auto [v, c] = e[i];
if (c > 0 && h[v] == -1) {
h[v] = h[u] + 1;
if (v == t) {
return true;
}
que.push(v);
}
}
}
return false;
}
T dfs(int u, int t, T f) {
if (u == t) {
return f;
}
auto r = f;
for (int &i = cur[u]; i < int(g[u].size()); ++i) {
const int j = g[u][i];
auto [v, c] = e[j];
if (c > 0 && h[v] == h[u] + 1) {
auto a = dfs(v, t, std::min(r, c));
e[j].cap -= a;
e[j ^ 1].cap += a;
r -= a;
if (r == 0) {
return f;
}
}
}
return f - r;
}
void addEdge(int u, int v, T c) {
g[u].push_back(e.size());
e.emplace_back(v, c);
g[v].push_back(e.size());
e.emplace_back(u, 0);
}
T flow(int s, int t) {
T ans = 0;
while (bfs(s, t)) {
cur.assign(n, 0);
ans += dfs(s, t, std::numeric_limits<T>::max());
}
return ans;
}
std::vector<bool> minCut() {
std::vector<bool> c(n);
for (int i = 0; i < n; i++) {
c[i] = (h[i] != -1);
}
return c;
}
struct Edge {
int from;
int to;
T cap;
T flow;
};
std::vector<Edge> edges() {
std::vector<Edge> a;
for (int i = 0; i < e.size(); i += 2) {
Edge x;
x.from = e[i + 1].to;
x.to = e[i].to;
x.cap = e[i].cap + e[i + 1].cap;
x.flow = e[i + 1].cap;
a.push_back(x);
}
return a;
}
};
void solve() {
int n, b, q;
std::cin >> n >> b >> q;
std::vector<std::pair<int, int>> a(q);
for (int i = 0; i < q; ++ i) {
std::cin >> a[i].first >> a[i].second;
}
a.push_back({b, n});
++ q;
std::sort(a.begin(), a.end());
for (int i = 0; i + 1 < q; ++ i) {
if (a[i + 1].second < a[i].second || a[i + 1].second - a[i].second > a[i + 1].first - a[i].first) {
std::cout << "unfair\n";
return;
}
}
const int N = b + q + 11;
MaxFlow<int> f(N);
int s = N - 1, t = N - 2;
for (int i = 1; i <= 5; ++ i) {
f.addEdge(b + i, t, n / 5);
}
for (int i = 1; i <= b; ++ i) {
f.addEdge(i, b + i % 5 + 1, 1);
}
for (int i = 0, last = 0, last_cnt = 0; i < q; ++ i) {
if (a[i].first == last) {
continue;
} else {
f.addEdge(s, b + 5 + i + 1, a[i].second - last_cnt);
for (int j = last + 1; j <= a[i].first; ++ j) {
f.addEdge(b + 5 + i + 1, j, 1);
}
last = a[i].first;
last_cnt = a[i].second;
}
}
if (f.flow(s, t) == n) {
std::cout << "fair\n";
} else {
std::cout << "unfair\n";
}
}