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


A. Commentary Boxes

题意:把\(n\)变成\(m\)的倍数,每次加一花费\(a\),每次加一花费\(b\)。求最小花费。

点击查看代码
void solve() {
    i64 n, m, a, b;
    std::cin >> n >> m >> a >> b;
    std::cout << std::min((n - n / m * m) * b, ((n + m - 1) / m * m - n) * a) << "\n";
}

B. Micro-World

题意:如果\(a_j < a_i \leq a_j + k\)。则可以删除\(a_j\)。求最多删除多少个。

从大到小排序。然后可以二分找每个数是不是有数可以删去它,也可以一个指针维护。

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

    std::ranges::sort(a);
    std::vector<int> st(n, 1);
    for (int i = 0, j = 0; i < n; ++ i) {
    	while (j < i && a[j] + k < a[i]) {
    		++ j;
    	}

    	while (j < i && a[i] > a[j]) {
    		st[j ++ ] = 0;
    	}
    }

    std::cout << std::ranges::count(st, 1) << "\n";
}

C. Bracket Sequences Concatenation Problem

题意:给你\(n\)个括号序列,选两个序列拼接起来,求有多少拼法可以形成一个合法的序列。

求出每个序列作为右边拼接和左边拼接多出来的左右括号,然后记录下来每个左右括号的个数,最后相应的左右括号乘起来即可。

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

    auto checkl = [&](const std::string & s) -> int {
    	int m = s.size();
    	int sum = 0;
		for (int i = 0; i < m; ++ i) {
			if (s[i] == '(') {
				++ sum;
			} else {
				-- sum;
			}

			if (sum < 0) {
				return -1;
			}
		}

		return sum;
    };

    auto checkr = [&](const std::string & s) -> int {
    	int m = s.size();
    	int sum = 0;
		for (int i = m - 1; i >= 0; -- i) {
			if (s[i] == ')') {
				++ sum;
			} else {
				-- sum;
			}

			if (sum < 0) {
				return -1;
			}
		}

		return sum;
    };


    std::map<int, int> cnt1, cnt2;
    i64 ans = 0;
    for (int i = 0; i < n; ++ i) {
		int l = checkl(s[i]), r = checkr(s[i]);
		if (l != -1) {
			++ cnt1[l];
		} 

		if (r != -1) {
			++ cnt2[r];
		}
    }

    for (auto & [x, y] : cnt1) {
    	ans += (i64)y * cnt2[x];
    }

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

D. Graph And Its Complement

题意:构造一个\(n\)个点的图。使得这个图有\(a\)个联通块,其补图有\(b\)个联通块。

每个图和它的补图至少一个只有联通块。假设有\(x\)联通块,那么在补图里不同联通块里的点都能两两连边,只可能有一个联通块。
那么我们就可以随便构造了,如果\(b=1\),则把\([1, a-1]\)单独一个点,然后后面的点都会\(a\)连边。如果\(a=1\),那么一样的构造,然后取反就行。
需要特判的是\(a = 1, b = 1\)的情况。这个情况我们需要构造一条链,然后\(n=2,3\)的情况是无解的。

点击查看代码
void solve() {
    int n, a, b;
    std::cin >> n >> a >> b;
    if (a > 1 && b > 1 || (a == 1 && b == 1 && (n == 2 || n == 3))) {
    	std::cout << "NO\n";
    	return;
    }

    std::vector ans(n, std::vector<int>(n));
    if (a == 1 && b == 1) {
    	for (int i = 0; i + 1 < n; ++ i) {
    		ans[i][i + 1] = ans[i + 1][i] = 1;
    	}
    } else if (a > 1) {
    	for (int i = a; i < n; ++ i) {
    		ans[i][a - 1] = ans[a - 1][i] = 1;
    	}
    } else {
    	for (int i = b; i < n; ++ i) {
    		ans[i][b - 1] = ans[b - 1][i] = 1;
    	}

    	for (int i = 0; i < n; ++ i) {
    		for (int j = 0; j < n; ++ j) {
    			if (i != j) {
    				ans[i][j] ^= 1;
    			}
    		}
    	}
    }

    std::cout << "YES\n";
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < n; ++ j) {
    		std::cout << ans[i][j];
    	}
    	std::cout << "\n";
    }
}

E. Post Lamps

题意:一个\([0, n]\)的数轴,有\(k\)种路灯,第\(i\)种路灯一个价格为\(a_i\),可以照明\([x, x + i]\)这个区间。有些点不能放路灯,求照明\([0, n]\)的最小代价。

这题其实就是直接暴力接下来。因为对于路灯\(i\),我们每次可以跳\(i\),那么总共跳\(\frac{n}{i}\),直接暴力复杂度加起来是一个调和级数。不过需要处理有些点不能放路灯的情况,我们预处理一个\(pre_i\)表示\([1, i]\)距离\(i\)最近的可以放路灯的地方。

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

    std::vector<int> a(k + 1);
    for (int i = 1; i <= k; ++ i) {
    	std::cin >> a[i];
    }

    if (st[0] == 1) {
    	std::cout << -1 << "\n";
    	return;
    }

    std::vector<int> pre(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	if (st[i] == 0) {
    		pre[i] = i;
    	} else {
    		pre[i] = pre[i - 1];
    	}
    }

    i64 ans = 1e18;
    for (int i = 1; i <= k; ++ i) {
    	i64 sum = 0;
    	for (int j = 0; j <= n;) {
    		sum += a[i];
    		if (j + i >= n) {
    			break;
    		}

			if (pre[j + i] == j) {
				sum = 1e18;
				break;
			} else {
				j = pre[j + i];
			}
    	}

    	ans = std::min(ans, sum);
    } 

    if (ans == 1e18) {
    	ans = -1;
    }
    std::cout << ans << "\n";
}

F. Flow Control

题意:给你一个图,每个边有流量,给出每个点接受流量减支出流量的值,构造满足条件的边的流量。

如果想要满足条件,我这个点支出了这么多流量,则其它点就要接受这么多流量。则每个点的流量之和应该为\(0\)
然后我们可以随便选一棵树,在树上讨论父节点要给多少流量给儿子就行。注意流量的正负和边的方向有关,需要记录一下。

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

    int m;
    std::cin >> m;
    std::vector<std::vector<std::pair<int, int>>> adj(n);
    for (int i = 1; i <= m; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].emplace_back(v, i);
    	adj[v].emplace_back(u, -i);
    }

    if (std::accumulate(s.begin(), s.end(), 0) != 0) {
    	std::cout << "Impossible\n";
    	return;
    }

    std::vector<int> vis(n);
    std::vector<int> f(m + 1);
    auto dfs = [&](auto & self, int u, int fa, int id) -> void {
    	vis[u] = 1;
    	for (auto & [v, id] : adj[u]) {
    		if (vis[v]) {
    			continue;
    		}

    		self(self, v, u, id);
    	}

    	if (fa != -1) {
    		if (id > 0) {
    			f[id] += s[u];
    			s[fa] += s[u];
    		} else {
    			f[-id] -= s[u];
    			s[fa] += s[u];
    		}
    	}
    };

    dfs(dfs, 0, -1, -1);
    std::cout << "Possible\n";
    for (int i = 1; i <= m; ++ i) {
    	std::cout << f[i] << "\n";
    }
}
posted @ 2025-03-28 21:48  maburb  阅读(10)  评论(0)    收藏  举报