VP Educational Codeforces Round 42 (Rated for Div. 2)


A. Equator

题意:求最前的前缀其和大于等于总和一半。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    int sum = std::accumulate(a.begin(), a.end(), 0);
    sum = (sum + 1) / 2;
    for (int i = 0; i < n; ++ i) {
    	sum -= a[i];
    	if (sum <= 0) {
    		std::cout << i + 1 << "\n";
    		return;
    	}
    }
}

B. Students in Railway Carriage

题意:长度为\(n\)的数组,有些地方能放东西有些不能。有两种类型的东西分别有\(a, b\)个,两个相同类型的不能相邻,求最多放多少个。

以两个不能放的位置分为一个个区间,每个区间讨论一下。如果区间长度是奇数,则有一个可以多放,应该让数量多的放。

点击查看代码
void solve() {
    int n, a, b;
    std::cin >> n >> a >> b;
    std::string s;
    std::cin >> s;
    s = "*" + s + "*";
    n += 2;
    int ans = 0;
    for (int i = 0; i + 1 < n && (a || b); ++ i) {
    	int j = i + 1;
    	while (s[j] != '*') {
    		++ j;
    	}

    	int cnt = j - i - 1;
    	if (a < b) {
    		std::swap(a, b);
    	}

    	if (b > 0) {
    		int k = std::min(b, cnt / 2);
    		b -= k;
    		ans += k;
    	}

    	if (a > 0) {
    		int k = std::min(a, cnt - cnt / 2);
    		a -= k;
    		ans += k;
    	}

    	i = j - 1;
    }

    std::cout << ans << "\n";
}

C. Make a Square

题意:给你一个\(n\),你要通过删除一些数字把它变成平方数。求删除的数最小。

\(n\)\(int\)范围内。那么我们直接枚举平方数,然后把两个数转为字符串就行子序列匹配,判断\(n\)是否可以变成这个平方数。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s = std::to_string(n);
    int m = s.size();
    int ans = -1;
    for (int i = 1; 1LL * i * i <= n; ++ i) {
    	std::string t = std::to_string(i * i);
    	int k = t.size();
    	int y = 0;
    	for (int x = 0; x < m; ++ x) {
    		if (s[x] == t[y]) {
    			++ y;
    		}
    	}

    	if (y == k) {
    		if (ans == -1 || m - k < ans) {
    			ans = m - k;
    		}
    	}
    }

    std::cout << ans << "\n";
}

D. Merge Equals

题意:给你一个数组,每次把个数大于等于两个的数里的最小数取出前两个,并删除第一个,把第二个变成两倍。求最后的数组。

模拟。
\(set\)记录值和下标,以及用一个\(map\)记录每个数的出现次数。每次操作时如果最小的数个数小于2就加入答案。发现取出最小的两个数,把他们变成两倍插到后面那个数的位置,更新\(set\)\(map\)

点击查看代码
void solve() {
	int n;
	std::cin >> n;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
	}

	std::set<std::pair<i64, int>> s;
	std::map<i64, int> cnt;
	for (int i = 0; i < n; ++ i) {
		s.insert({a[i], i});
		++ cnt[a[i]];
	}

	std::set<std::pair<int, i64>> ans;
	while (s.size()) {
		while (s.size() && cnt[s.begin()->first] < 2) {
			ans.insert({s.begin()->second, s.begin()->first});
			s.erase(s.begin());
		}

		if (s.empty()) {
			break;
		}

		auto [v, p1] = *s.begin();
		s.erase(s.begin());
		auto [_, p2] = *s.begin();
		s.erase(s.begin());
		cnt[v] -= 2;
		s.insert({v * 2, p2});
		cnt[v * 2] += 1;
	}

	std::cout << ans.size() << "\n";
	for (auto & it : ans) {
		std::cout << it.second << " \n"[it == *ans.rbegin()];
	}
}

E. Byteland, Berland and Disputed Cities

题意:数轴上有\(n\)个点,点的类型有三种,要求你给点对之间建边,边权为点的距离,使得删去第二种类型的点或删去第三种类型的点后依然联通。

直接两个点分开讨论,然后在相邻的点依次连边是错误。因为如果是\(PRBP\)这种,分开讨论就是两个\(P\)之间的距离乘二,但其实我们可以先连接两个\(P\),然后中间的点连一边代价更小。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::pair<i64, std::string>> a(n);
    
    int L = n, R = -1;
    for (int i = 0; i < n; ++ i) {
    	i64 x;
    	std::string t;
    	std::cin >> x >> t;
    	if (t == "P") {
    		L = std::min(L, i);
    		R = std::max(R, i);
    	}
    	a[i] = {x, t};
    }

    auto get = [&](int l, int r) -> i64 {
		i64 l1 = 1e18, r1 = -1e18, l2 = 1e18, r2 = -1e18;
    	for (int i = l; i <= r; ++ i) {
    		if (a[i].second == "P") {
    			l1 = std::min(l1, a[i].first);
    			r1 = std::max(r1, a[i].first);
    			l2 = std::min(l2, a[i].first);
    			r2 = std::max(r2, a[i].first);
    		} else if (a[i].second == "B") {
    			l1 = std::min(l1, a[i].first);
    			r1 = std::max(r1, a[i].first);
    		} else {
    			l2 = std::min(l2, a[i].first);
    			r2 = std::max(r2, a[i].first);
    		}
    	}

    	return std::max<i64>(0, r1 - l1) + std::max<i64>(0, r2 - l2);
    };

    auto work = [&](std::vector<i64> & a, i64 l, i64 r) -> i64 {
    	if (a.empty()) {
    		return 0;
    	}

    	i64 res = 1e18;
    	for (int i = 0; i + 1 < a.size(); ++ i) {
    		res = std::min(res, a[i] - l + r - a[i + 1]);
    	}

    	return std::min({res, r - a[0], a.back() - l});
    };

    if (L >= R) {
    	std::cout << get(0, n - 1) << "\n";
    	return;
    }

    i64 ans = get(0, L) + get(R, n - 1);
    for (int i = L; i < R; ++ i) {
    	int j = i + 1;
    	std::vector<i64> b, r;
    	while (a[j].second != "P") {
    		if (a[j].second == "B") {
    			b.push_back(a[j].first);
    		} else {
    			r.push_back(a[j].first);
    		}
    		++ j;
    	}

    	i64 x = a[i].first, y = a[j].first;
	    ans += std::min((y - x) * 2, y - x + work(b, x, y) + work(r, x, y));

    	i = j - 1;
    }

    std::cout << ans << "\n";
}

F. Simple Cycles Edges

题意:给你一个图,找有多少边恰好在一个环里。

关于环的问题,我们想到\(tarjan\)求联通分量,通过猜以及观察样例,如果一个点双里只有一个环,那么这个环上的点都满足条件。
那么我们先求点双。那么我们如何知道每个点双里有多少边呢?可以和记录点一样,开一个栈,每次正向搜索的时候把边加入这个栈,那么遇到断点我们就通过弹栈把这些边弹出来就行。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<std::vector<std::pair<int, int>>> adj(n);
    for (int i = 0; i < m; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].emplace_back(v, i);
    	adj[v].emplace_back(u, i);
    }

    std::vector<int> dfn(n), low(n), id(n), stk(n + 10), stk1(m + 10), cut(n);
    int top = 0, top1 = 0, idx = 0, dcc_cnt = 0;
    std::vector<std::vector<int>> dcc(n), dcc_edges(n);

    int root;
   	auto tarjan = [&](auto & self, int u, int fa) -> void {
   		dfn[u] = low[u] = ++ idx;
		stk[ ++ top] = u; 

		int flag = 0;
		for (auto & [v, edge_id] : adj[u]) {
			if (!dfn[v]) {
				stk1[ ++ top1] = edge_id;
				self(self, v, u);
				low[u] = std::min(low[u], low[v]);
				if (dfn[u] <= low[v]) {
					if (u != root || ++ flag > 1) {
						cut[u] = 1;
					}

					int x;
					do {
						x = stk[top -- ];
						id[x] = dcc_cnt;
						dcc[dcc_cnt].push_back(x);
					} while (x != v);

					do {
						x = stk1[top1 --];
						dcc_edges[dcc_cnt].push_back(x);
					} while (x != edge_id);

					dcc[dcc_cnt].push_back(u); 
	                id[u] = dcc_cnt;	
					++ dcc_cnt;
				}
			} else if (v != fa){
				low[u] = std::min(low[u], dfn[v]);
				if (dfn[v] < dfn[u]) {
					stk1[ ++ top1] = edge_id;
				}
			}
		}
   	};

   	for (int i = 0; i < n; ++ i) {
   		if (!dfn[i]) {
   			if (adj[i].empty()) {
   				id[i] = dcc_cnt;
   				dcc[dcc_cnt ++ ].push_back(i);
   			} else {
   				root = i;
   				tarjan(tarjan, i, -1);
   			}
   		}
   	}

   	std::vector<int> ans;
   	for (int i = 0; i < dcc_cnt; ++ i) {
   		if (dcc[i].size() == dcc_edges[i].size()) {
   			for (auto & j : dcc_edges[i]) {
   				ans.push_back(j);
   			}
   		}
   	}

   	std::sort(ans.begin(), ans.end());
   	std::cout << ans.size() << "\n";
   	for (auto & x : ans) {
   		std::cout << x + 1 << " ";
   	}
   	std::cout << "\n";
}
posted @ 2025-03-26 17:52  maburb  阅读(7)  评论(0)    收藏  举报