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";
   	}
}
posted @ 2025-04-05 15:15  maburb  阅读(15)  评论(0)    收藏  举报