VP Educational Codeforces Round 51 (Rated for Div. 2)
A. Vasya And Password
分类讨论
点击查看代码
void solve() {
std::string s;
std::cin >> s;
int f1 = 0, f2 = 0, f3 = 0;
for (auto & c : s) {
f1 += c >= 'a' && c <= 'z';
f2 += c >= 'A' && c <= 'Z';
f3 += c >= '0' && c <= '9';
}
if (!f1 && !f2) {
s[0] = 'a'; s[1] = 'A';
} else if (!f1 && !f3) {
s[0] = 'a'; s[1] = '0';
} else if (!f2 && !f3) {
s[0] = 'A'; s[1] = '0';
} else if (!f1) {
for (auto & c : s) {
if (c >= 'A' && c <= 'Z' && f2 > 1) {
c = 'a';
break;
}
if (c >= '0' && c <= '9' && f3 > 1) {
c = 'a';
break;
}
}
} else if (!f2) {
for (auto & c : s) {
if (c >= 'a' && c <= 'z' && f1 > 1) {
c = 'A';
break;
}
if (c >= '0' && c <= '9' && f3 > 1) {
c = 'A';
break;
}
}
} else if (!f3) {
for (auto & c : s) {
if (c >= 'A' && c <= 'Z' && f2 > 1) {
c = '0';
break;
}
if (c >= 'a' && c <= 'z' && f1 > 1) {
c = '0';
break;
}
}
}
std::cout << s << "\n";
}
B. Relatively Prime Pairs
相邻两个数一定互质。
点击查看代码
void solve() {
i64 l, r;
std::cin >> l >> r;
std::cout << "YES\n";
for (i64 i = l; i <= r; i += 2) {
std::cout << i << " " << i + 1 << "\n";
}
}
C. Vasya and Multisets
题意:把\(n\)个数放到两个多重集合里,一个集合的价值定义为其中恰好出现一次的数的个数,你要使得两个集合价值相等。
数组里只出现一次的数显然会使得其中一个数组价值加一。出现两次的则不会影响相对价值,因为如果两个集合分别放一个那么两个集合价值都加一,相当于没变。否则出现了三次及以上,可以都放一个集合使得两个集合价值都不变,或者放一个到一个集合,其它放另一个集合,使得其中一个集合价值加一。
那么先平均分配只出现一次的数,如果是偶数个,则用出现三次以上的给价值少的集合加一。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::map<int, int> mp;
for (auto & c : a) {
++ mp[c];
}
int cnt = 0, cnt1 = 0;
for (auto & c : a) {
if (mp[c] == 1) {
++ cnt;
}
if (mp[c] > 2) {
cnt1 += 1;
}
}
if (cnt % 2 == 1 && !cnt1) {
std::cout << "NO\n";
return;
}
std::string s(n, 'A');
int flag = cnt & 1;
for (int i = 0, j = 0; i < n; ++ i) {
if (mp[a[i]] == 1 && j < cnt / 2) {
s[i] = 'B';
++ j;
}
if (mp[a[i]] > 2 && flag) {
s[i] = 'B';
flag = 0;
}
}
std::cout << "YES\n";
std::cout << s << "\n";
}
D. Bicolorings
题意:给你一个\(2\times n\)的矩阵,给它黑白染色,使得连通块数量恰好是\(k\)的方案数是多少?
考虑\(dp\),我们按列\(dp\),记\(f[i][j][0/1/2/3]\)为第\(i\)列有\(k\)个连通块,当前状态为\(0/1/2/3\)的方案数。其中\(0/1/2/3\)为一列状态的二进制表示,如果这一列第一行染黑色则第1位1,第二行染黑色第2位为1。
那么可得转移方程:
\(f[i][j][0] = f[i - 1][j][0] + f[i - 1][j][1] + f[i - 1][j][2] + f[i - 1][j - 1][3]\)
\(f[i][j][1] = f[i - 1][j - 1][0] + f[i - 1][j][1] + f[i - 1][j - 2][2] + f[i - 1][j - 1][3]\)
\(f[i][j][2] = f[i - 1][j - 1][0] + f[i - 1][j - 2][1] + f[i - 1][j][2] + f[i - 1][j - 1][3]\)
\(f[i][j][3] = f[i - 1][j - 1][0] + f[i - 1][j][1] + f[i - 1][j][2] + f[i - 1][j][3]\)
代码省略取模类。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
if (k == 1) {
std::cout << 2 << "\n";
return;
}
std::vector f(n + 1, std::vector(k + 1, std::array<Z, 4>{0, 0, 0, 0}));
f[1][1][0] = 1; f[1][2][1] = 1;
f[1][2][2] = 1; f[1][1][3] = 1;
for (int i = 2; i <= n; ++ i) {
for (int j = 1; j <= k; ++ j) {
//00 00 10 10
//00 10 00 10
f[i][j][0] = f[i - 1][j][0] + f[i - 1][j][1] + f[i - 1][j][2] + f[i - 1][j - 1][3];
//00 00 10 10
//01 11 01 11
if (j > 1) {
f[i][j][1] = f[i - 1][j - 1][0] + f[i - 1][j][1] + f[i - 1][j - 2][2] + f[i - 1][j - 1][3];
}
//01 01 11 11
//00 10 00 10
if (j > 1) {
f[i][j][2] = f[i - 1][j - 1][0] + f[i - 1][j - 2][1] + f[i - 1][j][2] + f[i - 1][j - 1][3];
}
//01 01 11 11
//01 11 01 11
f[i][j][3] = f[i - 1][j - 1][0] + f[i - 1][j][1] + f[i - 1][j][2] + f[i - 1][j][3];
}
}
std::cout << f[n][k][0] + f[n][k][1] + f[n][k][2] + f[n][k][3] << "\n";
}
E. Vasya and Big Integers
待补。
F. The Shortest Statement
题意:给你一个图,每次求两个点的最短路。特性性质\(m - n \leq 20\)。
从特性性质下手,发现如果我们把图搞一个生成树,那么最多有20条边不在树上,这些边最多连接40个不同的点。那么我们可以对这些点进行一次\(dijkstra\)。对生成树做\(lca\)两点路径。
那么两点最短路就分两种情况去最小值。
点击查看代码
struct DSU {
std::vector<int> fa, cnt;
DSU(int _n) {
init(_n);
}
void init(int _n) {
fa.assign(_n, 0);
cnt.assign(_n, 1);
std::iota(fa.begin(), fa.end(), 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) {
return false;
}
fa[y] = x;
cnt[x] += cnt[y];
return true;
}
bool same(int x, int y) {
return find(x) == find(y);
}
int size(int x) {
return cnt[find(x)];
}
};
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::vector<std::pair<int, int>>> adj(n + 1), adj_tree(n + 1);
std::vector<std::array<int, 3>> edges(m);
for (int i = 0; i < m; ++ i) {
int u, v, w;
std::cin >> u >> v >> w;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
edges[i] = {w, u, v};
}
std::ranges::sort(edges);
DSU dsu(n + 1);
std::vector<int> a;
for (auto & [w, u, v] : edges) {
if (dsu.same(u, v)) {
a.push_back(u);
a.push_back(v);
continue;
}
dsu.merge(u, v);
adj_tree[u].push_back({v, w});
adj_tree[v].push_back({u, w});
}
std::vector<int> d(n + 1);
const int lg = std::__lg(n) + 1;
std::vector f(n + 1, std::vector<int>(lg + 1));
std::vector<i64> fd(n + 1);
std::queue<int> q;
q.push(1);
d[1] = 1;
while (q.size()) {
int u = q.front(); q.pop();
for (auto & [v, w] : adj_tree[u]) {
if (!d[v]) {
d[v] = d[u] + 1;
fd[v] = fd[u] + w;
f[v][0] = u;
for (int i = 1; i <= lg; ++ i) {
f[v][i] = f[f[v][i - 1]][i - 1];
}
q.push(v);
}
}
}
auto lca = [&](int x, int y) -> int {
if (d[x] < d[y]) {
std::swap(x, y);
}
for (int i = lg; i >= 0; -- i) {
if (d[f[x][i]] >= d[y]) {
x = f[x][i];
}
}
if (x == y) {
return x;
}
for (int i = lg; i >= 0; -- i) {
if (f[x][i] != f[y][i]) {
x = f[x][i];
y = f[y][i];
}
}
return f[x][0];
};
const i64 inf = 1e18;
auto dijkstra = [&](std::vector<i64> & dist, int s) -> void {
using PII = std::pair<i64, int>;
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> heap;
std::ranges::fill(dist, inf);
dist[s] = 0;
heap.emplace(dist[s], s);
while (heap.size()) {
auto [d, u] = heap.top(); heap.pop();
if (d != dist[u]) {
continue;
}
for (auto & [v, w] : adj[u]) {
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
heap.emplace(dist[v], v);
}
}
}
};
std::ranges::sort(a);
a.erase(std::unique(a.begin(), a.end()), a.end());
int k = a.size();
std::vector dist(k, std::vector<i64>(n + 1));
for (int i = 0; i < k; ++ i) {
dijkstra(dist[i], a[i]);
}
int Q;
std::cin >> Q;
while (Q -- ) {
int u, v;
std::cin >> u >> v;
i64 ans = inf;
for (int i = 0; i < k; ++ i) {
ans = std::min({ans, dist[i][u] + dist[i][v]});
}
ans = std::min(ans, fd[u] + fd[v] - 2 * fd[lca(u, v)]);
std::cout << ans << "\n";
}
}